You are on page 1of 20

Using Jenkins/Hudson for Continuous Builds

King Foo Jonas Marin 29 March 2011, Zend Webinar

This talk: live action ... on remote servers ... so, things can go *boom*

Info has been added to these slides, mostly snippets of configuration items showed during the webinar.

What is CI? What is Hudson & Jenkins ?

topics

Install 1 jenkins master

Jenkins: install, start & configure Build project (ZF Quickstart; from SVN) Add plugins + tips Install slave Jenkins Build project (ZF Quickstart)

Install 1 jenkins slave


Build a Symfony based project (from SVN) if some time is left for this

Links

http://hudson-ci.org/ http://jenkins-ci.org/ http://pkg.jenkins-ci.org/debian/ http://framework.zend.com/manual/en/learning .quickstart.intro.html http://www.symfony-project.org/jobeet/1_4 https://github.com/akrabat/Akrabat http://www.phing.info/docs/guide/current/ http://en.wikipedia.org/wiki/Jenkins_(software)

Install jenkins

add to /etc/apt/sources: deb http://pkg.jenkins-ci.org/debian binary/ then: wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add sudo apt-get update sudo apt-get install jenkins

Setup vhost

We created and are going to build a ZF project based on the Quickstart (using MySQL instead of SQLite as the backend. Install Apache & MySQL & PHP packages: apt-get install apache2 mysql-server mysql-common php5 php5-mysql php-pear \ php5-curl php5-xdebug Create a DB user with appropriate rights for the project. After project BuildZF has been build for the first time: mkdir /web ln -s /var/lib/jenkins/jobs/BuildZF/workspace /web/jenkinsdemo That's for ease of use. Then set appropriate ownerships for the Apache user. Jenkins checks code out using jenkins as user and adm as group. We added www-data (Debian Apache user) to the adm group in /etc/group, also for ease of use. Put an appropriate Apache vhost pointing to /web/jenkinsdemo & enable the site and the BuildZF project is available for the outside world (you can restrict access using a firewall or .htaccess of course).

Setup tools

Phing, phpUnit, PHP_Depend, phpdoc:


pear pear pear pear pear pear pear pear pear pear upgrade-all channel-discover pear.phpunit.de channel-discover components.ez.no channel-discover pear.symfony-project.com install --alldeps phpunit/PHPUnit channel-discover pear.phing.info install --alldeps phing/phing channel-discover pear.pdepend.org install --alldeps pdepend/PHP_Depend install --alldeps PhpDocumentor

Optional:
pear pear pear pear pear install phpunit/phploc channel-discover pear.phpmd.org channel-discover pear.pdepend.org install --alldeps phpmd/PHP_PMD install PHP_CodeSniffer

For each of these: trigger command using Phing (specific task or shell script) or use a shell command + let Jenkins know where to find the necessary info for reporting.
PHP tool PhpUnit PHP_Depend phpDocumentor ... and many more Jenkins plugin xUnit & Clover plugin jDepend JavaDoc

Phing build.xml

<?xml version="1.0" ?> <project name="Test Project" default="test"> <property name="tests.dir" value="../../tests" /> <property name="reports.dir" value="${tests.dir}/reports/" /> <property name="site.dir" value="../../" /> <target name="test"> <echo msg="Unittests" /> <exec command="phpunit --bootstrap=./bootstrap.php --configuration ./phpunit.xml --coverage-clover ./log/report/coverage/clover.xml --coverage-html ./log/report/coverage/ --colors" dir="$ {tests.dir}" passthru="true" /> </target> <target name="phpdoc"> <phpdoc title="API Documentation" destdir="../../tests/docs" sourcecode="yes" defaultpackagename="BuildZF" output="HTML:Smarty:PHP"> <fileset dir="../../application" id="ZF Application"> <include name="**/*.php" /> </fileset> <!--<fileset dir="../../library/Zend" id="ZF Library"> <include name="**/*.php" /> </fileset>--> </phpdoc> </target> <target name="pdepend"> <phpdepend> <fileset dir="${site.dir}"> <include name="application/**/*.php" /> <!--<include name="library/Zend/**/*.php" />--> </fileset> <logger type="jdepend-xml" outfile="../../tests/log/pdepend.xml"/> <logger type="jdepend-chart" outfile="../../tests/log/dependencies.svg"/> <logger type="overview-pyramid" outfile="../../tests/log/overview-pyramid.svg"/> <analyzer type="coderank-mode" value="method"/> </phpdepend> </target> </project>

Plugins/tools

Akrabat db upgrades phpUnit + coverage PHP_Depend phpcs Phpdoc others:

phpcpd phpmd php_codebrowser

Setup Akrabat
Db migration tool for Zend Framework. See https://github.com/akrabat/Akrabat and akrabat.com. Use local zf alias and tell zf to store config in the active directory (handy when having multiple projects on 1 server): alias zf='export ZF_HOME=.; ./bin/zf.sh' zf create config zf --setup storage-directory zf --setup config-file Edit .zf.ini so that it looks like this: php.include_path = "/web/jenkinsdemo/library/Akrabat:/web/jenkinsdemo/library:.:/usr/share/php:/usr/share/pear" basicloader.classes.0 = "Akrabat_Tool_DatabaseSchemaProvider" Now you can use this in the job configuration screen (add shell command): cd $WORKSPACE; export ZF_HOME=. ; ./bin/zf.sh update database-schema build ./scripts/migration WARNING: when using the last command, there is no error feedback. WARNING: build is the environment used for the vhost being updated by jenkins. It should match APPLICATION_ENV. This makes the build fail and is probably more useful: #!/usr/bin/php <?php chdir($_SERVER['WORKSPACE']); $result=`export ZF_HOME=.;./bin/zf.sh update database-schema build ./scripts/migration`; echo $result; if (stristr($result,'AN ERROR HAS OCCURED')) { exit(1); } exit(0);

Akrabat up script

scripts/migration/0001-initiatedb.php <?php class initiatedb extends Akrabat_Db_Schema_AbstractChange { function up() { $sql = "CREATE TABLE IF NOT EXISTS guestbook ( id int(11) NOT NULL AUTO_INCREMENT, email varchar(64) NOT NULL DEFAULT 'example@example.com', comment text, created datetime NOT NULL, PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8"; $this->_db->query($sql); $sql = "INSERT INTO `guestbook` VALUES ('','ralph.schindler@zend.com','Hello! Hope you enjoy this sample zf application!',NOW()), ('','foo@bar.com','Baz baz baz, baz baz Baz baz baz - baz baz baz.',NOW()), ('','jonas@king-foo.be','test a comment',NOW())"; $this->_db->query($sql); } function down() { $sql = "DROP TABLE IF EXISTS guestbook"; $this->_db->query($sql); } }

By the way, upgrading from Hudson to Jenkins is easy too..

PHP_Depend

pdepend images outputted on the job's result page, add this to the description field:
<embed height="300" src="http://jenkinsdemo2.kingfoo.net:8080/job/BuildZF/ws/tests/log/overview-pyramid.svg" type="image/svg+xml" width="500"></embed> <embed height="300" src="http://jenkinsdemo2.kingfoo.net:8080/job/BuildZF/ws/tests/log/dependencies.svg" type="image/svg+xml" width="500"></embed>

!! Thanks to Sebastian Bergmann and http://jenkins-php.org/ for the idea...

Jenkins slave

Use separate machine for the builds (or many of them) Create a public/private key for the jenkins user (in /var/lib/jenkins/.ssh/) and upload the public key into the root or another privileged user's account on the slave machine. Use the GUI for adding a slave. You can specify the private key or enter the password for the root user on the slave machine. Jenkins will install a slave process and even download and install Java JRE if necessary. Matching a job to a slave node can be done using labels (and regex). The slave machine should have Apache, MySQL and the extra tools, just like the master (if you want the same project to be build on the slave instead of the master)

Symfony

A potential starting point:


<?xml version="1.0" encoding="UTF-8"?> <project name="symfonydemo" basedir="." default="app"> <target name="ownbywebserver"> <exec command="chown -R hudson.adm ." dir="/var/lib/jenkins/workspace/BuildSymfony/trunk" /> <exec command="chmod -R g+w ." dir="/var/lib/jenkins/workspace/BuildSymfony/trunk" /> </target> <target name="updateplugins"> <foreach param="dirname" absparam="absdirname" target="updateplugin"> <fileset dir="/var/lib/jenkins/workspace/BuildSymfony/trunk/plugins"> <include name="*Plugin"/> </fileset> </foreach> </target> <target name="updateplugin"> <svnupdate svnpath="/usr/bin/svn" todir="/var/lib/jenkins/workspace/BuildSymfony/trunk/plugins/${absdirname}" /> </target> <target name="reloaddb"> <exec command="rm -rf *" dir="/var/lib/jenkins/workspace/BuildSymfony/trunk/cache/" /> <exec command="./symfony doctrine:build --all --and-load --env=staging no-confirmation" dir="/var/lib/jenkins/workspace/BuildSymfony/trunk" logoutput="true" /> </target> </project>

Alternatives

Bamboo CruiseControl/phpUnderControl Arbit Xinc (project seems dead however)

inspiration

http://jenkins.ci.servergrove.com/job/Imagine/ http://jenkins-php.org/ https://github.com/sebastianbergmann/php-jenkins-template https://github.com/sebastianbergmann/php-project-wizard http://blog.jepamedia.org/2009/10/28/continuous-integration-forphp-with-hudson/ http://www.davegardner.me.uk/blog/2009/11/09/continuousintegration-for-php-using-hudson-and-phing/ http://stackoverflow.com/questions/2603414/nabaztagalternatives http://stackoverflow.com/questions/303614/whats-your-favoriteextreme-feedback-device

Thanks for your time Questions?

jonas@king-foo.be www.king-foo.be www.king-foo.be/blog @jonasmarien

You might also like