Phing 101 / How to staff a build orchestra   #phpuceu11Sonntag, 20. Februar 2011
Session agenda       Introduction       Phing in Action       Bending Phing to your needs       Testing in build land     ...
What‘s that Phingy thingy?       External build DSL and it‘s interpreter       Extensible build framework       Heavily in...
When to pick Phing (over Ant / Rake / ...)       Let‘s you stay in PHP land       Let‘s you use PHP and PHP land API‘s    ...
Installing the conductor         $ pear channel-discover pear.phing.info         $ pear install phing/phing         $ pear...
Setting the stage         <?xml version="1.0" encoding="UTF-8"?>         <project name="phpuceu" default="localize" basedi...
Phing in Action         <?xml version="1.0" encoding="UTF-8"?>         <project name="phpuceu" default="localize" basedir=...
Phing in Action         <?xml version="1.0" encoding="UTF-8"?>         <project name="phpuceu" default="localize" basedir=...
Phing in Action         <?xml version="1.0" encoding="UTF-8"?>         <project name="phpuceu" default="localize" basedir=...
How Phing empowers CI and CD         <?php         exec(phing -f faulty-build.xml 2>&1, $output, $returnCode);         var...
How Phing empowers CI and CDSonntag, 20. Februar 2011
Why / when to automate       Identify repetitive, error-prone, and       boring tasks       Avoid repeated man tool and   ...
Picking your orchestra staff       Get to know the official Phing       guide       Pick the tasks suitable for your       ...
Bending Phing to your needs       Wrap Cli commands with the       exec task         <?xml version="1.0" encoding="UTF-8"?...
Bending Phing to your needs       Adhoc tasks         <?xml version="1.0" encoding="UTF-8"?>         <project name="phpuce...
Bending Phing to your needs       Custom tasks         <?php         require_once phing/Task.php;         require_once phi...
Bending Phing to your needs       Using custom tasks         <?xml version="1.0" encoding="UTF-8"?>         <project name=...
Transforming custom tasks       into official ones       Connect with Michiel Rook to get       Svn commit privileges      ...
Crafting custom build listeners       Implement Phing‘s BuildListener       interface       Make it known to the Phing Cli...
Crafting custom build listeners        <?php        class BuildhawkLogger extends DefaultLogger {          private $_gitNo...
Using custom build listeners        $ phing -logger phing.listener.BuildhawkLogger         $ buildhawk --title Examplr > e...
Testing tasks and buildfiles       Unit tests for custom tasks       Structure and execution tests for       the buildfile /...
Testing tasks and buildfiles          class GitHubIssuesTaskTest extends PHPUnit_Framework_TestCase {               private...
Testing tasks and buildfiles          class GitHubIssuesTaskTest extends PHPUnit_Framework_TestCase          {             ...
Testing tasks and buildfiles       Structure and execution tests for       the buildfile / build itself         /**         ...
Testing tasks and buildfiles       Structure and execution tests for       the buildfile / build itself         /**         ...
Testing tasks and buildfiles       Structure and execution tests for       the buildfile / build itself         /**         ...
Refactoring buildfiles     Extract target      <target name="test-mongodb-daos"              description="Testing MongoDb b...
Refactoring buildfiles       Extract target       Introduce property file         <property name="mongo.test.db" value="orch...
Refactoring buildfiles       Extract target       Introduce property file       Replace comment with description          <!...
Refactoring buildfiles       Extract target       Introduce property file       Replace comment with description       Enfor...
Refactoring buildfiles       Extract target       Introduce property file       Replace comment with description       Enfor...
Best practices       Follow an iterative approach       Apply the Martin Fowler‘s / Don       Roberts “Rule of Three“     ...
Alternatives in PHP land       taskman       pake       phake         <?php         desc("Say hello");         task(hello,...
Alternatives in PHP land       taskman       pake       phake         <?php         desc("Say hello");         task(hello,...
Alternatives in PHP land       taskman       pake       phake         <?php         desc("Say hello");         task(hello,...
Brought to you by                                    @raphaelstoltSonntag, 20. Februar 2011
Image credits       [1] http://www.flickr.com/photos/seosmh/1352680740       [2] http://www.flickr.com/photos/shoppingdiva/3...
Links & Resources       [1] http://phing.info       [2] ‘Refactoring Ant buildfiles‘ essay, Julian Simpson in           The...
Upcoming SlideShare
Loading in...5
×

Phing101 or How to staff a build orchestra

3,457

Published on

Talk about PHP's buildtool Phing and how to bend it to your needs.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
3,457
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
21
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Phing101 or How to staff a build orchestra

  1. 1. Phing 101 / How to staff a build orchestra #phpuceu11Sonntag, 20. Februar 2011
  2. 2. Session agenda Introduction Phing in Action Bending Phing to your needs Testing in build land Refactoring buildfiles Best practices Alternatives in PHP landSonntag, 20. Februar 2011
  3. 3. What‘s that Phingy thingy? External build DSL and it‘s interpreter Extensible build framework Heavily influenced by Apache Ant Enables one button push automation Empowers CI and CD Tool that can take care of your repetitive and boring tasks Cli AdapterSonntag, 20. Februar 2011
  4. 4. When to pick Phing (over Ant / Rake / ...) Let‘s you stay in PHP land Let‘s you use PHP and PHP land API‘s when extending it No additional dependencies added (Java runtime, Ruby interpreter) Ships with currently ≈ 95 tasksSonntag, 20. Februar 2011
  5. 5. Installing the conductor $ pear channel-discover pear.phing.info $ pear install phing/phing $ pear install -a phing/phing $ phing -vSonntag, 20. Februar 2011
  6. 6. Setting the stage <?xml version="1.0" encoding="UTF-8"?> <project name="phpuceu" default="localize" basedir="."> <property name="city.name" value="Manchester" override="true"/> <target name="localize" depends="hello" description="Localize city"> <echo msg="to ${city.name}!"/> </target> <target name="hello" description="Say hello"> <echo msg="Phing says hello"/> </target> </project>Sonntag, 20. Februar 2011
  7. 7. Phing in Action <?xml version="1.0" encoding="UTF-8"?> <project name="phpuceu" default="localize" basedir="."> <property name="city.name" value="Manchester" override="true"/> <target name="localize" depends="hello" description="Localize city"> <echo msg="to ${city.name}!"/> </target> <target name="hello" description="Say hello"> <echo msg="Phing says hello"/> </target> </project>Sonntag, 20. Februar 2011
  8. 8. Phing in Action <?xml version="1.0" encoding="UTF-8"?> <project name="phpuceu" default="localize" basedir="."> <property name="city.name" value="Manchester" override="true"/> <target name="localize" depends="hello" description="Localize city"> <echo msg="to ${city.name}!"/> </target> <target name="hello" description="Say hello"> <echo msg="Phing says hello"/> </target> </project>Sonntag, 20. Februar 2011
  9. 9. Phing in Action <?xml version="1.0" encoding="UTF-8"?> <project name="phpuceu" default="localize" basedir="."> <property name="city.name" value="Manchester" override="true"/> <target name="localize" depends="hello" description="Localize city"> <echo msg="to ${city.name}!"/> </target> <target name="hello" description="Say hello"> <echo msg="Phing says hello"/> </target> </project>Sonntag, 20. Februar 2011
  10. 10. How Phing empowers CI and CD <?php exec(phing -f faulty-build.xml 2>&1, $output, $returnCode); var_dump($returnCode); exec(phing -f successful-build.xml 2>&1, $output, $returnCode); var_dump($returnCode);Sonntag, 20. Februar 2011
  11. 11. How Phing empowers CI and CDSonntag, 20. Februar 2011
  12. 12. Why / when to automate Identify repetitive, error-prone, and boring tasks Avoid repeated man tool and tool --help look ups Have a simple Cli Adapter, to avoid the ‘bash hustle™‘ Save time, increase efficiencySonntag, 20. Februar 2011
  13. 13. Picking your orchestra staff Get to know the official Phing guide Pick the tasks suitable for your identified problem domain Bundle them in callable targets Define the orchestration flowSonntag, 20. Februar 2011
  14. 14. Bending Phing to your needs Wrap Cli commands with the exec task <?xml version="1.0" encoding="UTF-8"?> <project name="phpuceu" default="setup-mongodb-test-data" basedir="."> <property name="mongo.test.db" value="orchestra-test" override="true" /> <property name="mongo.test.data.file" value="${project.basedir}/fixtures/orchestra.json" /> <property name="mongo.test.data.collection" value="testcollection" /> <target name="setup-mongodb-test-data" description="Setting up MongoDb test data"> <exec command="mongoimport --db ${mongo.test.db} --collection ${mongo.test.data.collection} --file ${mongo.test.data.file}" logoutput="true" /> </target> </project>Sonntag, 20. Februar 2011
  15. 15. Bending Phing to your needs Adhoc tasks <?xml version="1.0" encoding="UTF-8"?> <project name="phpuceu" default="overview" basedir="."> <target name="adhoc-setup" description="Adhoc task(s) setup"> <adhoc-task name="git-commits-by-date"><![CDATA[ class GitCommitsByDate extends Task { private $date; function setDate($date) { $this->date = $date; } function main() { $gitCommand = sprintf( git log --since="%s" --oneline, $this->date ); exec($gitCommand, $logs); foreach ($logs as $log) { $this->log($log); } } }]]> </adhoc-task> </target> <target name="todays-commits" depends="adhoc-setup" description="Gather todays Git commits"> <git-commits-by-date date="2011-02-20" /> </target> </project>Sonntag, 20. Februar 2011
  16. 16. Bending Phing to your needs Custom tasks <?php require_once phing/Task.php; require_once phing/BuildException.php; class GitTagTask extends Task { private $annotation; public function setAnnotation($annotation) { $this->annotation = $annotation; } public function init() {} public function main() { $this->_validateProperties(); $gitCommand = sprintf("git tag -a %s -m ", $this->annotation); $this->log(Executing "%s", $gitCommand); exec($gitCommand); $project = $this->getProject(); $logMsg = sprintf(Tagged %s with %s, $project->getName(), $this->annotation); $this->log($logMsg); } /** * @throws Phing/BuildException */ private function _validateProperties() {} }Sonntag, 20. Februar 2011
  17. 17. Bending Phing to your needs Using custom tasks <?xml version="1.0" encoding="UTF-8"?> <project name="phpuceu" default="build" basedir="."> <taskdef name="git-tag" classname="phing-tasks.GitTagTask" /> <property name="tagable.build" value="false" override="true" /> <property name="tag.annotation" value="" override="true" /> <target name="-tag-project"> <if> <equals arg1="${tagable.build}" arg2="true" /> <then> <git-tag annotation="${tag.annotation}"/> </then> <else> <echo message="Skipping project tagging" /> </else> </if> </target> <target name="build" depends="inspect,test,-tag-project"> <echo msg="Building..." /> </target> <target name="inspect"> <echo msg="Inspecting..." /> </target> <target name="test"> <echo msg="Testing..." /> </target> </project>Sonntag, 20. Februar 2011
  18. 18. Transforming custom tasks into official ones Connect with Michiel Rook to get Svn commit privileges Add task and entourage to $PHING_TRUNK/task/ext/* Add task to $PHING_TRUNK/ tasks/defaults.properties Add PEAR packages depen- dencies to $PHING_TRUNK/build/ BuildPhingPEARPackageTask.php Document task & commitSonntag, 20. Februar 2011
  19. 19. Crafting custom build listeners Implement Phing‘s BuildListener interface Make it known to the Phing Cli at runtime with the -logger option To use it permanently add it to the Phing bash scriptSonntag, 20. Februar 2011
  20. 20. Crafting custom build listeners <?php class BuildhawkLogger extends DefaultLogger { private $_gitNotesCommandResponse = null; public function buildFinished(BuildEvent $event) { parent::buildFinished($event); if ($this->_isProjectGitDriven($event)) { $error = $event->getException(); if ($error === null) { $buildtimeForBuildhawk = $this->_formatBuildhawkTime( Phing::currentTimeMillis() - $this->startTime); if (!$this->_addBuildTimeAsGitNote($buildtimeForBuildhawk)) { $message = sprintf("Failed to add git note due to %s", $this->_gitNotesCommandResponse); $this->printMessage($message, $this->err, Project::MSG_ERR); } } } } private function _isProjectGitDriven(BuildEvent $event) { $project = $event->getProject(); $projectGitDir = sprintf(%s/.git, $project->getBasedir()->getPath()); return file_exists($projectGitDir) && is_dir($projectGitDir); } private function _formatBuildhawkTime($micros) { return sprintf("%0.3f", $micros); } private function _addBuildTimeAsGitNote($buildTime) { $gitCommand = sprintf("git notes --ref=buildtime add -f -m %s HEAD 2>&1", $buildTime); $gitNotesCommandResponse = exec($gitCommand, $output, $return); if ($return !== 0) { $this->_gitNotesCommandResponse = $gitNotesCommandResponse; return false; } return true;Sonntag, 20. } Februar 2011
  21. 21. Using custom build listeners $ phing -logger phing.listener.BuildhawkLogger $ buildhawk --title Examplr > examplr-build-times.htmlSonntag, 20. Februar 2011
  22. 22. Testing tasks and buildfiles Unit tests for custom tasks Structure and execution tests for the buildfile / build itselfSonntag, 20. Februar 2011
  23. 23. Testing tasks and buildfiles class GitHubIssuesTaskTest extends PHPUnit_Framework_TestCase { private $task; private $testReportDirectory; Integration, formal tests for the public function setUp() { $this->task = new GitHubIssuesTask; buildfile itself $this->task->init(); $this->testReportDirectory = TEST_DIR . DIRECTORY_SEPARATOR . test-ghi-dir; } /** * @test * @expectedException BuildException * @dataProvider nonAcceptedReportTypesProvider */ public function taskShouldThrowExceptionOnNonAcceptedReportTypes($type) { $this->task->setReportType($type); $this->task->main(); } * @return array */ public function nonAcceptedReportTypesProvider() { return array( array(doc), array(vid), array(pdf), ); } }Sonntag, 20. Februar 2011
  24. 24. Testing tasks and buildfiles class GitHubIssuesTaskTest extends PHPUnit_Framework_TestCase { private $task; private $testReportDirectory; Integration, formal tests for the public function setUp() { buildfile itself } // omitted /** * @test */ public function taskShouldCreateReportDirectoryWhenNotExisting() { Phing::setProperty(host.fstype, UNIX); $this->assertFalse(file_exists($this->testReportDirectory)); $this->task->setReportType(txt); $this->task->setRepository(name:repos); $this->task->setReportDirectory($this->testReportDirectory); $project = $this->getMock(Projekt, array(logObject)); $project->expects($this->once()) ->method(logObject) ->with($this->anything()); $this->task->setProject($project); $this->task->main(); $this->assertTrue(file_exists($this->testReportDirectory)); $this->assertTrue(is_dir($this->testReportDirectory)); $this->assertTrue(rmdir($this->testReportDirectory)); } }Sonntag, 20. Februar 2011
  25. 25. Testing tasks and buildfiles Structure and execution tests for the buildfile / build itself /** * @test * @group structure */ public function buildfileShouldContainACleanTarget() { $xml = new SimpleXMLElement($this->_buildfileXml); $cleanElement = $xml->xpath("//target[@name=clean]"); $this->assertTrue(count($cleanElement) === 0, "Buildfile doesnt contain a clean target"); }Sonntag, 20. Februar 2011
  26. 26. Testing tasks and buildfiles Structure and execution tests for the buildfile / build itself /** * @test * @group structure */ public function targetBuildShouldDependOnCleanTarget() { $xml = new SimpleXMLElement($this->_buildfileXml); $xpath = "//target[@name=build]/@depends"; $dependElement = $xml->xpath($xpath); $this->assertTrue(count($dependElement) > 0, Target build contains no depends attribute ); $dependantTasks = array_filter(explode( , trim($dependElement[0]->depends)) ); $this->assertContains(clean, $dependantTasks, "Target build doesnt depend on the clean target" ); }Sonntag, 20. Februar 2011
  27. 27. Testing tasks and buildfiles Structure and execution tests for the buildfile / build itself /** * @test * @group execution */ public function initTargetShouldCreateInitialBuildArtifacts() { $this->_isTearDownNecessary = true; $this->_buildfileRunner->runTarget(array(init)); $expectedInitArtifacts = array( "{$this->_buildfileBasedir}/build", "{$this->_buildfileBasedir}/build/logs/performance/", "{$this->_buildfileBasedir}/build/doc", "{$this->_buildfileBasedir}/build/reports" ); foreach ($expectedInitArtifacts as $artifact) { $this->assertFileExists( $artifact, "Expected file {$artifact} doesnt exist" ); } }Sonntag, 20. Februar 2011
  28. 28. Refactoring buildfiles Extract target <target name="test-mongodb-daos" description="Testing MongoDb based Daos"> <exec command="mongoimport --db ${mongo.test.db} --collection ${mongo.test.data.collection} --file ${mongo.test.data.file}" logoutput="true" /> <exec command="phpunit --configuration mongodb-test.xml" dir="${test.directory}" logoutput="true" /> </target> <target name="setup-mongodb-test-data" description="Setting up MongoDb test data"> <exec command="mongoimport --db ${mongo.test.db} --collection ${mongo.test.data.collection} --file ${mongo.test.data.file}" logoutput="true" /> </target> <target name="test-mongodb-daos" depends="setup-mongodb-test-data" description="Testing MongoDb based Daos"> <exec command="phpunit --configuration mongodb-test.xml" dir="${test.directory}" logoutput="true" /> </target>Sonntag, 20. Februar 2011
  29. 29. Refactoring buildfiles Extract target Introduce property file <property name="mongo.test.db" value="orchestra" override="true" /> <property name="mongo.test.data.file" value="${project.basedir}/fixtures/orchestra.json" /> <property name="mongo.test.data.collection" value="testcollection" /> <property file="mongodb-test.properties" /> + [mongodb-test.properties] mongo.test.db=orchestra mongo.test.data.file=fixtures/orchestra.json mongo.test.data.collectionSonntag, 20. Februar 2011
  30. 30. Refactoring buildfiles Extract target Introduce property file Replace comment with description <!-- Target runs the code base against PHP_CodeSniffer ruleset --> <target name="inspect" depends="lint"> <phpcodesniffer ... /> </target> <target name="inspect" depends="lint" description="Sniff code base against PHP_CodeSniffer ruleset"> <phpcodesniffer ... /> </target>Sonntag, 20. Februar 2011
  31. 31. Refactoring buildfiles Extract target Introduce property file Replace comment with description Enforce internal target <target name="deploy" depends="test-setup, test, smoke-test" description="Deploys the project into ..."> <!-- omitted --> </target> <target name="-deploy" depends="test-setup, test, smoke-test" description="Deploys the project into ..."> <!-- omitted --> </target>Sonntag, 20. Februar 2011
  32. 32. Refactoring buildfiles Extract target Introduce property file Replace comment with description Enforce internal target Introduce distinct target naming <property name="deployment-mode" value="staging" override="true" /> <target name="cloneGitRepos"> <!-- ... --> </target> <property name="deployment.mode" value="staging" override="true" /> <target name="clone-git-repos"> <!-- ... --> </target>Sonntag, 20. Februar 2011
  33. 33. Best practices Follow an iterative approach Apply the Martin Fowler‘s / Don Roberts “Rule of Three“ Describe your targets Threat buildfiles as ‘first-class‘ code citizens Establish a common buildfile coding standardSonntag, 20. Februar 2011
  34. 34. Alternatives in PHP land taskman pake phake <?php desc("Say hello"); task(hello, function() { echo Phake says hello ; }); desc("Localize city"); task(localize,hello, function($app) { $cityName = Manchester; if (isset($app[city.name]) && $app[city.name] !== ) { $cityName = $app[city.name]; } echo to . $cityName . ! . PHP_EOL; }); task(default, localize);Sonntag, 20. Februar 2011
  35. 35. Alternatives in PHP land taskman pake phake <?php desc("Say hello"); task(hello, function() { echo Phake says hello ; }); desc("Localize city"); task(localize,hello, function($app) { $cityName = Manchester; if (isset($app[city.name]) && $app[city.name] !== ) { $cityName = $app[city.name]; } echo to . $cityName . ! . PHP_EOL; }); task(default, localize);Sonntag, 20. Februar 2011
  36. 36. Alternatives in PHP land taskman pake phake <?php desc("Say hello"); task(hello, function() { echo Phake says hello ; }); desc("Localize city"); task(localize,hello, function($app) { $cityName = Manchester; if (isset($app[city.name]) && $app[city.name] !== ) { $cityName = $app[city.name]; } echo to . $cityName . ! . PHP_EOL; }); task(default, localize);Sonntag, 20. Februar 2011
  37. 37. Brought to you by @raphaelstoltSonntag, 20. Februar 2011
  38. 38. Image credits [1] http://www.flickr.com/photos/seosmh/1352680740 [2] http://www.flickr.com/photos/shoppingdiva/3695515673 [3] http://www.flickr.com/photos/rotterdamsphilharmonischorkest/ [4] http://www.flickr.com/photos/clarazamith/3816864822 [5] http://www.flickr.com/photos/rodrigodavid/4074212668 [6] http://www.flickr.com/photos/melitron/4923829202 [7] http://www.flickr.com/photos/52569650@N00/4310444273 [8] http://www.flickr.com/photos/crazytales562/3013076129 [9] http://www.flickr.com/photos/roland/1924450950Sonntag, 20. Februar 2011
  39. 39. Links & Resources [1] http://phing.info [2] ‘Refactoring Ant buildfiles‘ essay, Julian Simpson in The ThoughtWorks® Anthology by Pragmatic Programmers [3] http://code.google.com/p/lib-taskman/ [4] https://github.com/indeyets/pake [5] https://github.com/jaz303/phakeSonntag, 20. Februar 2011
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×