Saturday, December 29, 2007

BuildBot: Continuous Integration System

I've spent some of this holiday season learning how to set up BuildBot (http://buildbot.net/) which is a Continuous Integration system that is especially aimed at open-source style projects: You set up a central "build master", and one or more "build slaves" - and it is very easy for someone to set up a new build slave, so if you have some new platform you want to test a project on you can add a build slave. The build-master admin has to add your slave on the master side, and the slave needs to be able to access the source code repository (CVS in my case.)

I've got three of my Perl projects from my CVS repo running under BuildBot now, and it's all working, except email notification of build status - no email is getting to the mail server, and i have yet to learn how to debug that.

When I commit a change to my CVS repository for one of these projects that kicks off a build on one or more build slaves, which checks out the latest code, and runs all the unit tests. The build master shows the results in a web page.

I've got about 9.5 hours into it, (out of an initial estimate of 12) include various yak-shaving activities.

For now I have the build status pages for the three projects at:
  • Perl-Metrics-Simple - Building using both MakeMaker and Module::Build. This distro provides modules for a utility program to count lines, packages, subs and complexity of Perl files.
  • DBIx-Wrapper-VerySimple - Simplify use of DBI.
  • Text-TagTemplate - Lightweight flexible template parsing module.
See also my earlier posting with a review of a book on Continuous Integration and a link to a comparison of several CI systems.

2007-12-29 update: Starting to add notes to the Perl-QA Wiki.
2008-01-06 update: The email issue was caused by a typo in my master.cfg file.
subject='%(builder) BUILD STATUS', WRONG
subject='%(builder)s BUILD STATUS', CORRECT
Note the missing 's' after the closing parenthesis - it is part of a  Python extended printf statement.
Also, I have now added a fourth buildmaster which builds the Parrot project. All my buildbot configurations are now at: http://www.eigenstate.net/buildbot/masters.html
2008-01-17 update: The open source webkit project uses buildbot - you can see their build status at http://build.webkit.org/

Technorati Tags: , ,

Saturday, December 01, 2007

Continuous Integration - Improving Software Quality and Reducing Risk

Paul Duvall, Steve Matyas, and Andrew Glover have written a fine book describing the value and practice of Continuous Integration or CI for short. If you have heard of CI and want to learn more about it, or if you want to help educate others about it, this book is a very good place to start.

Continuous Integration is a software development practice intended to notify the development team as soon as possible when a defect is introduced. Typically when CI is being used there is an automated system which the builds the entire project many times each day from its source code to its complete form, and all its automated tests and other automated quality assurance tools can be brought to bear.

The authors of "Continuous Integration" repeatedly emphasize the role that CI has in reducing risk in software development and constantly provide examples of specific practices that support and benefit from CI, for example frequent commits to a version control system, automated tests,  automated code analysis (test coverage, code complexity, duplication, etc.)

I think this book would be great for a leader that is trying to convince their team or their management of the value of CI, as well as for a team implementing CI for the first time as an aid to deciding what system to choose and what aspects to implement first.

Continuous Integration: Improving Software Quality and Reducing Risk
by Paul Duvall, Steve Matyas, Andrew Glover
Paperback: 336 pages
Publisher: Addison-Wesley Professional (July 9, 2007)
Language: English
ISBN-10: 0321336380
ISBN-13: 978-0321336385

See also:

Technorati Tags: , , , ,

Monday, November 19, 2007

Time Machine Very Slow on Leopard

Time Machine will run very slowly if an anti-virus utility such as Norton is scanning each file as it is written or changed on the backup disk.

The fix for this is to go into the Norton Auto-Protect Preference Pane of System Preferences and add your backup disk in the SafeZones tab.

Technorati Tags:

Saturday, September 08, 2007

cyradm fails: Can't locate auto/Cyrus/IMAP/imclient_ha.al in @INC

Attempting to login via cyradm fails because of a missing library.


UPDATE 2007-09-15: I believe I have a fix for this. In Cyrus/IMAP/Shell.pm change line 780 from:

if (Cyrus::IMAP::imclient_havetls()) {

to

if (Cyrus::IMAP::havetls()) {


See also: https://bugzilla.andrew.cmu.edu/show_bug.cgi?id=2988

Name : cyrus-imapd
Arch : x86_64
Version: 2.3.9
Release: 6.fc6

$ uname -a
Linux galadriel 2.6.22.1-32.fc6 #1 SMP Wed Aug 1 14:30:16 EDT 2007 x86_64
x86_64 x86_64 GNU/Linux

$ cyradm
cyradm> login matisse
Can't locate auto/Cyrus/IMAP/imclient_ha.al in @INC (@INC contains:
/usr/lib64/perl5/site_perl/5.8.8/x86_64-linux-thread-multi
/usr/lib64/perl5/site_perl/5.8.7/x86_64-linux-thread-multi
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi
/usr/lib64/perl5/site_perl/5.8.5/x86_64-linux-thread-multi
/usr/lib/perl5/site_perl/5.8.8 /usr/lib/perl5/site_perl/5.8.7
/usr/lib/perl5/site_perl/5.8.6 /usr/lib/perl5/site_perl/5.8.5
/usr/lib/perl5/site_perl
/usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi
/usr/lib64/perl5/vendor_perl/5.8.7/x86_64-linux-thread-multi
/usr/lib64/perl5/vendor_perl/5.8.6/x86_64-linux-thread-multi
/usr/lib64/perl5/vendor_perl/5.8.5/x86_64-linux-thread-multi
/usr/lib/perl5/vendor_perl/5.8.8 /usr/lib/perl5/vendor_perl/5.8.7
/usr/lib/perl5/vendor_perl/5.8.6 /usr/lib/perl5/vendor_perl/5.8.5
/usr/lib/perl5/vendor_perl /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi
/usr/lib/perl5/5.8.8 .) at
/usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi/Cyrus/IMAP/Shell.pm
line 780

cyradm>

Evidently I am not the only one getting this problem. See: http://use.perl.org/~jk2addict/journal/32136

Thursday, May 17, 2007

Social Dynamics of Pair Programming

The Social Dynamics of Pair Programming

Interesting paper by Jan Chong and Tom Hurlbutt at Stanford University.

Here's the Abstract:
This paper presents data from a four month ethno-
graphic study of professional pair programmers from
two software development teams. Contrary to the cur-
rent conception of pair programmers, the pairs in this
study did not hew to the separate roles of “driver” and
“navigator”. Instead, the observed programmers
moved together through different phases of the task,
considering and discussing issues at the same strategic
“range” or level of abstraction and in largely the same
role. This form of interaction was reinforced by fre-
quent switches in keyboard control during pairing and
the use of dual keyboards. The distribution of expertise
among the members of a pair had a strong influence on
the tenor of pair programming interaction. Keyboard
control had a consistent secondary effect on decision-
making within the pair. These findings have implica-
tions for software development managers and practi-
tioners as well as for the design of software develop-
ment tools.
(Link to PDF version or Google HTML view)

Technorati Tags: , , ,

Sunday, May 06, 2007

Finding which jar file provides a Java class.

Given a class name such as:

org.springframework.mail.javamail.MimeMessageHelper

www.jarfinder.com provides a fast easy way to find out which .jar file(s) provide the class, with links to the organization and download site for each one.

For example:

http://www.jarfinder.com/?class=org.springframework.mail.javamail.SmartMimeMessage&submit=search

Technorati Tags:

Friday, April 20, 2007

Estimating Value of a Possible Software Project

What factors to consider when estimating the value of a possible software project?

I made a list various factors I have considered, more or less formally as the case may be, when estimating the value of undertaking a development project.
When I thought about how I measure these factors I found that I generally think of them as each having a probability-distribution.
Instead of "This feature will cut response time in half", something like "This has a 90% chance of getting of cutting response time by 10%, and maybe a 30% chance of cutting it by 60%."
What factors do you find are most worth spending time to consider?
How might you express those factors in making a go/no-go decision on a project?

Examples, in no particular order:

Value Factors: What will we get from this software?

  • capabilities - but then I ask, what is the value of each capability?
  • What is the "total market available" for this software/feature?
  • How much of that market can we expect to get?
  • What new markets will this get us into?
  • What cost(s) will this feature help us avoid or reduce?
  • What value does this bring to our team - new skills, experience, etc.
  • How much fund will this be?
  • Synergy with other projects?
  • What is the time-to-market value? (what is the value of releasing early vs. later?) Taken to the extreme, that question turns into a cost question: "What is the cost of not releasing at all?"
  • Reputation value: What value does this bring to our brand?
Cost Factors: What will it cost us?
  • Labor costs
  • Hardware costs
  • Licensing costs
  • Costs associated with various risk factors:
  • Reputation cost for overreaching.
  • Human cost (burn-out, staff turnover) of doing too much.
  • New or increased legal or financial liabilities.
  • Delay to other projects.

Technorati Tags: ,

Sunday, January 21, 2007

JUnit-Style XML from Perl Test Files

Here is an experimental program that runs Perl test files and produces the same sort of XML output as the <junit> ant task. Getting this XML output is helpful when running Perl tests under CruiseControl.
#!/usr/bin/perl

use strict;
use warnings;

use Test::Harness::Straps;
use Time::HiRes qw(gettimeofday tv_interval);
use XML::Generator ':noimport';

my @files = @ARGV;

my $strap     = Test::Harness::Straps->new;
my $generator = XML::Generator->new(':pretty');

my @properties = _get_properties($generator);
my $test_results = _run_tests( $strap, $generator, @files );

my $xml = _get_junit_xml( $generator, \@properties, $test_results );

print "$xml\n";

exit;

#-------------------------------------------------------------------------------

sub _get_junit_xml {
    my ( $generator, $properties, $test_results ) = @_;

    my $system_out = 'system-out';
    my $system_err = 'system-err';
    my $xml        = '';
    $xml .= "\n";
    $xml .= $generator->testsuite(
        {
            errors   => 0,
            failures => $test_results->{total_failures},
            name     => 'name of the test suite',
            tests    => $test_results->{total_tests},
            'time'   => $test_results->{total_time},
        },
        $generator->properties(@properties),
        @{ $test_results->{test_cases} },
        $generator->$system_out(),
        $generator->$system_err(),

    );
    return $xml;
}

sub _run_tests {
    my ( $strap, $generator, @files ) = @_;
    my @test_cases     = ();
    my $total_tests    = 0;
    my $total_time     = 0;
    my $total_failures = 0;

    foreach my $test_file (@files) {
        my $start_time   = [gettimeofday];
        my $file_results = $strap->analyze_file($test_file);
        my $elapsed_time = tv_interval( $start_time, [gettimeofday] );
        $total_time += $elapsed_time;
        foreach my $assertion ( @{ $file_results->details } ) {
            if ( _skip($assertion) ) {
                next;
            }
            $total_tests++;
            $total_failures += _is_failure($assertion);
            my $test_case = {
                classname => $test_file,
                name      => $assertion->{name},
                'time'    => 0,
            };
            push @test_cases, $generator->testcase($test_case);
        }
    }
    my $test_results = {
        total_time     => $total_time,
        test_cases     => \@test_cases,
        total_tests    => $total_tests,
        total_failures => $total_failures,
    };
    return $test_results;
}

sub _skip {
    my $assertion = shift;
    return $assertion->{type} =~ / skip | todo /x;
}

sub _is_failure {
    my $assertion = shift;
    return !$assertion->{ok};
}

sub _get_properties {
    my $generator  = shift;
    my @properties = ();
    foreach my $key ( sort keys %ENV ) {
        push @properties,
          $generator->property( { name => "$key", value => $ENV{$key} } );
    }
    return @properties;
}

__END__

=head1 NAME

junit_xml.pl - Run Perl tests and get JUnit-style XML output

=head1 SYNOPSIS

   junit_xml.pl file1 [ file2 ... ]

=head1 DESCRIPTION

Experimental script to run perl test files and produce the
same XML output as produced by the <junit> ant task.

=head1 DEPENDENCIES

 Test::Harness
 Time::HiRes
 XML::Generator 

=head1 BUGS

  - Doesn't do anything with the STDERR from tests.
  - Doesn't fill in the 'errors' attribute in the  <testsuite> element.
  - Doesn't handle when all tests in file are skipped (skip_all)
  - Doesn't get the elapsed time for each 'test' (i.e. assertion.)


=head1 AUTHOR

Matisse Enzer <matisse@matisse.net>

=head1 COPYRIGHT & LICENSE

Copyright (c) 2007 Matisse Enzer. All Rights Reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
Updates: This post was originally published on 2007-01-21. 2008-05-26: I noticed that Justin Mason has created a better script for converting TAP output to JUnit-style XML

Technorati Tags: ,

Unix shell: Finding the run-time directory of a script

I've been trying to come up with an elegant way, in a shell script, to find the full path of the directory in which the script is located.

So far, I have this, which works for bash but not for sh (sh does not support substring expansion on variables):


 #!/usr/local/bin/bash -x
# Finds the full path to the directory containing this script

dir_containing_this_script=""
script_dirname=`dirname $0`
first_char="${script_dirname:0:1}" ; # Starting at index 0, 1 character
case "$first_char" in
("/")
# It's a full path. Use it unmodified.
dir_containing_this_script="${script_dirname}"
;;
(*)
# Strip leading . characters
while [ "${first_char}" = "." ]; do
script_dirname="${script_dirname:1}" ; # substring from index 1 to end
first_char="${script_dirname:0:1}"
done
current_dir=`pwd`
if [ -s "${script_dirname}" ] ; then
dir_containing_this_script="${current_dir}/${script_dirname}"
else
dir_containing_this_script="${current_dir}"
fi
;;
esac

echo "$dir_containing_this_script"


Update: Eric M on The WELL posted this most excellent solution:

dn=`dirname $0`
path=`(cd $dn; pwd)`
echo "path = $path"

Technorati Tags: , ,

Friday, January 19, 2007

Super Easy Graphics Programming

Eric Townsend pointed me to Processing - super easy programming environment for creating graphics programs.

Here's an applet I generated from it in a few minutes work, starting with an example Eric posted on The WELL.

/// start
void setup() {
size(600,600);
}

void draw(){
background(255);

int left_x = 0;
int right_x = width;
int top_y = 0;
int bottom_y = height;
int light_green = #99FF99;
int light_blue = #66CCFF;
int light_yellow = #FFCC66;
int pale_red = #FF3366;

for (int n=0; n < 100; n++) {
int y_of_n = n * (bottom_y/100);
int x_of_n = n * (right_x/100);

stroke(pale_red);
line(left_x,y_of_n,mouseX,mouseY);

stroke(light_yellow);
line(right_x,y_of_n,mouseX,mouseY);

stroke(light_blue);
line(x_of_n,0,mouseX,mouseY);

stroke(light_green);
line(x_of_n,bottom_y,mouseX,mouseY);
}
}

/// end

Technorati Tags: ,