Buildbot

From Perl QA
Jump to: navigation, search

SEMI-ROUGH NOTES - could use some editing and more examples.

Contents

About BuildBot

BuildBot is an open source Continuous Integration system which is especially suited to projects that want to do continuous integration on multiple architectures and operating systems.

In BuildBot there is a central build master and any number of build slaves

You can have multiple build slaves on a single machine, so for example, you could have three machines with 5 build slaves, like this:

  • Machine 1: Darwin 9.1.0 on i386
    • Build slave 1A: gcc version 4.0.1
    • Build slave 1B: gcc version 4.0.2
  • Machine 2: GNU/Linux 2.6.22.1-32.fc6 on x86_64
    • Build slave 2A: gcc version 4.1.2
  • Machine 3: SunOS 5.6 on sun4u sparc SUNW,Ultra-4
    • Build slave 3A: gcc version 4.0.1
    • Build slave 3B: gcc version 4.1.2

Installing and Configuring BuildBot

In BuildBot you configure a factory which has a series of steps to obtain source code and build it. Then you configure a builder which tells a particular slave to use a particular factory, so you might pass the same factory to slaves running on different architectures, and/or pass different factories (different build processes) to slaves running on the same machine, for example, one factory builds using Module::Build and the other uses MakeMaker.

Configuration For Building CPAN-style Distributions

Getting BuildBot to build a CPAN-style distribution is pretty simple.

Although BuildBot comes with a factory that knows how to build a MakeMaker distro, I created my own factory with a series of 3 steps so I could specify TEST_VERBOSE=1 for the MakeMaker build, and I created another factory with steps for building a distro using the more modern Module::Build.

The following configuration fragment creates two factories:

  • f1 is a factory that "knows" how to build a module using Module::Build
  • f2 is a factory that "knows" how to use MakeMaker and sets TEST_VERBOSE=1

I also created two builders - each "builder" is a hash that has a unique name and associates a factory with a build slave.

In the example below I use the same slave but different factories for each builder (see the slavename and factory entries in each builder.) I also specify a different builddir for each builder since they are using the same slave I don't want them stepping on each others' files.

Here is a fragment of master.cfg from the Perl::Metrics::Simple build master:

cvsroot = "/usr/local/CVS"
cvsmodule = 'Perl-Metrics-Simple'
builders = []

from buildbot.process import factory
from buildbot.steps.source import CVS
from buildbot.steps import shell
# from buildbot.steps.python_twisted import Trial
f1 = factory.BuildFactory()
f1.addStep(CVS,
           cvsroot=cvsroot, cvsmodule=cvsmodule,
           mode="clobber")
f1.addStep(shell.ShellCommand, command=["/usr/bin/perl", "Build.PL"])
f1.addStep(shell.ShellCommand, command=["./Build"])
f1.addStep(shell.ShellCommand, command=["./Build", "test", "verbose=1"])
#f1.addStep(Trial, testpath=".")

f2 = factory.BuildFactory()
f2.addStep(CVS,
           cvsroot=cvsroot, cvsmodule=cvsmodule,
           mode="clobber")
f2.addStep(shell.ShellCommand, command=["/usr/bin/perl", "Makefile.PL"])
f2.addStep(shell.ShellCommand, command=["make"])
f2.addStep(shell.ShellCommand, command=["make", "test", "TEST_VERBOSE=1"])

b1 = {'name': "Perl-Metrics-Simple-MB",
      'slavename': "perl-metrics-simple_bot1",
      'builddir': "Perl-Metrics-Simple-MB",
      'factory': f1,
      }
b2 = {'name': "Perl-Metrics-Simple-MM",
      'slavename': "perl-metrics-simple_bot1",
      'builddir': "Perl-Metrics-Simple-MM",
      'factory': f2,
      }
c['builders'] = [b1,b2]

Configuration For Building the Parrot Project

Here's a master.cfg example for a buildmaster for the Parrot project. The example has a single build slave portland dentist and two schedulers: one does a build every 24 hours (daily) and the other does a build when the source code changes (code_changed.) This configuration polls the parrot SVN repository every hour looking for changes. the code_changed scheduler will start a build if no new changes are received for ten minutes (the treeStableTimer setting.)

# Example of a buildbot master.cfg with one build slave
# for building the Parrot project.

admin_email_addr='nobody@example.com'
email_from_addr='buildmaster@example.com'
my_email_domain='example.com'

c = BuildmasterConfig = {}
c['debugPassword'] = "not_very_secure"
c['projectName'] = 'Parrot'
c['projectURL'] = 'http://www.perlfoundation.org/perl6/'
c['buildbotURL'] = "http://buildbot.eigenstate.net:8040/"
c['bots'] = [("parrot-bot1", "parrot-squawk")]
c['slavePortnum'] = 9987

# Where to get source code changes
from buildbot.changes.svnpoller import SVNPoller
source_code_svn_url='https://svn.perl.org/parrot/trunk'
svn_poller = SVNPoller(
                                svnurl=source_code_svn_url,
                                pollinterval=60*60, # seconds
                                histmax=10,
                                svnbin='/usr/bin/svn',
                              )
c['sources'] = [ svn_poller ]

# When to run builds
from buildbot import scheduler
daily = scheduler.Periodic("daily", ["Parrot-Builder"], 24*60*60)
code_changed = scheduler.Scheduler(name="code_changed", branch=None,
                                 treeStableTimer=10*60, # seconds
                                 builderNames=["Parrot-Builder"])
c['schedulers'] = [ daily,code_changed ]


# How to build the software
from buildbot.process import factory
from buildbot.steps import source,shell
f1 = factory.BuildFactory()
f1.addStep(source.SVN,
           svnurl=source_code_svn_url,
           mode="clobber")
f1.addStep(shell.ShellCommand, command=['perl', 'Configure.pl'])
f1.addStep(shell.ShellCommand, command=['make'])
f1.addStep(shell.ShellCommand, command=['make', 'test'])

b1 = {'name': 'Parrot-Builder',
      'slavename': 'parrot-bot1',
      'builddir': 'parrot_build_dir',
      'factory': f1,
      }
c['builders'] = [b1]


# How to report build status
c['status'] = []

from buildbot.status import mail
c['status'].append(mail.MailNotifier(fromaddr=email_from_addr,
                                     addLogs=True,
                                     mode='failing',
                                     relayhost='localhost',
                                     subject='%(builder)s BUILD FAILED',
                                     lookup=mail.Domain(my_email_domain),
                                     sendToInterestedUsers=True))
c['status'].append(mail.MailNotifier(fromaddr=email_from_addr,
                                     addLogs=False,
                                     mode='all',
                                     relayhost='smtp.example.com',
                                     subject='%(builder)s BUILD STATUS',
                                     extraRecipients=[admin_email_addr],
                                     lookup=mail.Domain(my_email_domain),
                                     sendToInterestedUsers=True))
from buildbot.status import html
waterfall = html.Waterfall(
           robots_txt='/home/buildmaster/robots.txt',
	   allowForce=False, http_port="tcp:8040:interface=10.0.0.23")
c['status'].append(waterfall)

Other Configuration and Setup Steps

Miscellaneous things you might need to do depending on your setup.

CVS: Notifying BuildBot of Changes

To notify BuildBot that a commit has happened in CVS I added the following to CVSROOT/loginfo:

Perl-Metrics-Simple /usr/bin/buildbot sendchange --master localhost:9989 --username $USER --logfile - %{s}

Added system startup scripts sacramento bankruptcy attorney for each build master and build slave. TODO: Maybe include these in this page and/or send to Brian Warner (author of BuildBot.)

Adding Build Artifacts to the Waterfall Status Page

For example, what if you have a factory that does a Build testcover and you want to make the test coverage HTML pages visible from the buildmaster status page.

TODO: I don't know how to do this yet. The answer probably lies somewhere in http://buildbot.net/repos/release/docs/buildbot.html#Writing-New-BuildSteps

Firewall: Opening a Hole in IPTables Configuration

I'm running my buildmaster on a Redhat FC6 machine. I added entries to /etc/sysconfig/iptables to allow access to the ports that the buildmaster status pages are served on:

 -A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp --dport 8010 -j ACCEPT

Make Build master or Slave Start on System Reboot

Here are a pair of scripts suitable for use on a system using the chkconfig utility.

Build Master System Startup Script


#!/bin/sh
#
# chkconfig: 345 95 15
# description: Starts and stops a buildbot master

PROJECT="parrot"
BB_USER="buildmaster"
MASTERS_DIR="/home/buildmaster/masters"
BASE_DIR="${MASTERS_DIR}/${PROJECT}"
BUILDBOT="/usr/bin/buildbot"

# Check that master.cfg exists.
[ -f "${BASE_DIR}/master.cfg" ] || exit $?

RETVAL=0

start() {
	printf "Starting buildbot master for %s\n" "$PROJECT"
        ACTION=start
        /bin/su $BB_USER -c "$BUILDBOT $ACTION $BASE_DIR"
	RETVAL=$?
	return $RETVAL
}

stop() {
	printf "Stopping buildbot master for %s\n" "$PROJECT"
        ACTION=stop
        /bin/su $BB_USER -c "$BUILDBOT $ACTION $BASE_DIR"
	RETVAL=$?
	return $RETVAL
}

restart() {
        stop
        start
}	

reload() {
        printf "Reconfiguring build master for %s\n" "$PROJECT"
        ACTION=reconfig
        /bin/su $BB_USER -c "$BUILDBOT $ACTION $BASE_DIR"
	RETVAL=$?
	return $RETVAL
}	

case "$1" in
  start)
  	start
	;;
  stop)
  	stop
	;;
  restart)
  	restart
	;;
  reload)
  	reload
	;;
  *)
	echo $"Usage: $0 {start|stop|restart|reload}"
	exit 1
esac

exit $?


Build Slave System Startup Script

#!/bin/sh
#
# chkconfig: 345 95 15
# description: Starts and stops a buildbot slave.

BB_USER="buildslave"
BASE_DIR="/home/buildslave/projects/parrot"
BUILDBOT="/usr/bin/buildbot"

RETVAL=0

start() {
	printf "Starting buildbot slave from %s\n" "$BASE_DIR"
        ACTION=start
        /bin/su $BB_USER -c "$BUILDBOT $ACTION $BASE_DIR"
	RETVAL=$?
	return $RETVAL
}	

stop() {
	printf "Stopping buildbot slave in %s\n" "$BASE_DIR"
        ACTION=stop
        /bin/su $BB_USER -c "$BUILDBOT $ACTION $BASE_DIR"
	RETVAL=$?
	return $RETVAL
}	

restart() {
        stop
        start
}	

case "$1" in
  start)
  	start
	;;
  stop)
  	stop
	;;
  restart)
  	restart
	;;
  *)
	echo $"Usage: $0 {start|stop|restart}"
	exit 1
esac

exit $?

Personal tools