SlideShare a Scribd company logo
1 of 39
Download to read offline
Phing 101 / How to staff a build orchestra   #phpuceu11

Sonntag, 20. Februar 2011
Session agenda

       Introduction
       Phing in Action
       Bending Phing to your needs
       Testing in build land
       Refactoring buildfiles
       Best practices
       Alternatives in PHP land


Sonntag, 20. Februar 2011
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 Adapter

Sonntag, 20. Februar 2011
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 tasks




Sonntag, 20. Februar 2011
Installing the conductor

         $ pear channel-discover pear.phing.info

         $ pear install phing/phing

         $ pear install -a phing/phing

         $ phing -v




Sonntag, 20. Februar 2011
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
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
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
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
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
How Phing empowers CI and CD




Sonntag, 20. Februar 2011
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 efficiency



Sonntag, 20. Februar 2011
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 flow




Sonntag, 20. Februar 2011
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
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
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
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
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 & commit
Sonntag, 20. Februar 2011
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 script




Sonntag, 20. Februar 2011
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
Using custom build listeners

        $ phing -logger phing.listener.BuildhawkLogger

         $ buildhawk --title 'Examplr' > examplr-build-times.html




Sonntag, 20. Februar 2011
Testing tasks and buildfiles

       Unit tests for custom tasks
       Structure and execution tests for
       the buildfile / build itself




Sonntag, 20. Februar 2011
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
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
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 doesn't contain a clean target");
         }




Sonntag, 20. Februar 2011
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 doesn't depend on the clean target"
              );
         }




Sonntag, 20. Februar 2011
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}' doesn't exist"
                   );
               }
         }



Sonntag, 20. Februar 2011
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
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.collection

Sonntag, 20. Februar 2011
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
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
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
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 standard


Sonntag, 20. Februar 2011
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
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
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
Brought to you by



                                    @raphaelstolt




Sonntag, 20. Februar 2011
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/1924450950



Sonntag, 20. Februar 2011
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/phake




Sonntag, 20. Februar 2011

More Related Content

What's hot

Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationKirill Chebunin
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Konstantin Kudryashov
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Michelangelo van Dam
 
Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo Evans Hauser
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowKacper Gunia
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
Introduction to Zend framework
Introduction to Zend framework Introduction to Zend framework
Introduction to Zend framework Matteo Magni
 
solving little problems
solving little problemssolving little problems
solving little problemsAustin Ziegler
 
Dealing with Continuous Data Processing, ConFoo 2012
Dealing with Continuous Data Processing, ConFoo 2012Dealing with Continuous Data Processing, ConFoo 2012
Dealing with Continuous Data Processing, ConFoo 2012Michael Peacock
 
OSGI workshop - Become A Certified Bundle Manager
OSGI workshop - Become A Certified Bundle ManagerOSGI workshop - Become A Certified Bundle Manager
OSGI workshop - Become A Certified Bundle ManagerSkills Matter
 

What's hot (20)

Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers Cracow
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Introduction to Zend framework
Introduction to Zend framework Introduction to Zend framework
Introduction to Zend framework
 
solving little problems
solving little problemssolving little problems
solving little problems
 
New in php 7
New in php 7New in php 7
New in php 7
 
Hooks WCSD12
Hooks WCSD12Hooks WCSD12
Hooks WCSD12
 
Dealing with Continuous Data Processing, ConFoo 2012
Dealing with Continuous Data Processing, ConFoo 2012Dealing with Continuous Data Processing, ConFoo 2012
Dealing with Continuous Data Processing, ConFoo 2012
 
CodeIgniter 3.0
CodeIgniter 3.0CodeIgniter 3.0
CodeIgniter 3.0
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
OSGI workshop - Become A Certified Bundle Manager
OSGI workshop - Become A Certified Bundle ManagerOSGI workshop - Become A Certified Bundle Manager
OSGI workshop - Become A Certified Bundle Manager
 

Similar to Phing101 or How to staff a build orchestra

Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014Eric Hogue
 
Building com Phing - 7Masters PHP
Building com Phing - 7Masters PHPBuilding com Phing - 7Masters PHP
Building com Phing - 7Masters PHPiMasters
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hopeMarcus Ramberg
 
Phing i Fabric - Budowanie i deployment aplikacji webowych
Phing i Fabric - Budowanie i deployment aplikacji webowychPhing i Fabric - Budowanie i deployment aplikacji webowych
Phing i Fabric - Budowanie i deployment aplikacji webowychleafnode
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Hugo Hamon
 
How to create a magento controller in magento extension
How to create a magento controller in magento extensionHow to create a magento controller in magento extension
How to create a magento controller in magento extensionHendy Irawan
 
Continous delivery with Jenkins and Chef
Continous delivery with Jenkins and ChefContinous delivery with Jenkins and Chef
Continous delivery with Jenkins and Chefdefrag2
 
Lean Php Presentation
Lean Php PresentationLean Php Presentation
Lean Php PresentationAlan Pinstein
 
Codeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationCodeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationAbdul Malik Ikhsan
 
Yves & Zed @ Developer Conference 2013
Yves & Zed @ Developer Conference 2013Yves & Zed @ Developer Conference 2013
Yves & Zed @ Developer Conference 2013FabianWesnerBerlin
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Michelangelo van Dam
 
AngularJS Directives
AngularJS DirectivesAngularJS Directives
AngularJS DirectivesEyal Vardi
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015Fernando Daciuk
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Michelangelo van Dam
 
Continuous Integration with Robot Sweatshop
Continuous Integration with Robot SweatshopContinuous Integration with Robot Sweatshop
Continuous Integration with Robot SweatshopJustin Scott
 

Similar to Phing101 or How to staff a build orchestra (20)

Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014
 
Building com Phing - 7Masters PHP
Building com Phing - 7Masters PHPBuilding com Phing - 7Masters PHP
Building com Phing - 7Masters PHP
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hope
 
Phing i Fabric - Budowanie i deployment aplikacji webowych
Phing i Fabric - Budowanie i deployment aplikacji webowychPhing i Fabric - Budowanie i deployment aplikacji webowych
Phing i Fabric - Budowanie i deployment aplikacji webowych
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2
 
Phing
PhingPhing
Phing
 
Play!ng with scala
Play!ng with scalaPlay!ng with scala
Play!ng with scala
 
Laravel 101
Laravel 101Laravel 101
Laravel 101
 
How to create a magento controller in magento extension
How to create a magento controller in magento extensionHow to create a magento controller in magento extension
How to create a magento controller in magento extension
 
Continous delivery with Jenkins and Chef
Continous delivery with Jenkins and ChefContinous delivery with Jenkins and Chef
Continous delivery with Jenkins and Chef
 
Lean Php Presentation
Lean Php PresentationLean Php Presentation
Lean Php Presentation
 
Codeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationCodeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept Implementation
 
Trimming The Cruft
Trimming The CruftTrimming The Cruft
Trimming The Cruft
 
Yves & Zed @ Developer Conference 2013
Yves & Zed @ Developer Conference 2013Yves & Zed @ Developer Conference 2013
Yves & Zed @ Developer Conference 2013
 
Write php deploy everywhere
Write php deploy everywhereWrite php deploy everywhere
Write php deploy everywhere
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
 
AngularJS Directives
AngularJS DirectivesAngularJS Directives
AngularJS Directives
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
 
Continuous Integration with Robot Sweatshop
Continuous Integration with Robot SweatshopContinuous Integration with Robot Sweatshop
Continuous Integration with Robot Sweatshop
 

Recently uploaded

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 

Recently uploaded (20)

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 

Phing101 or How to staff a build orchestra

  • 1. Phing 101 / How to staff a build orchestra #phpuceu11 Sonntag, 20. Februar 2011
  • 2. Session agenda Introduction Phing in Action Bending Phing to your needs Testing in build land Refactoring buildfiles Best practices Alternatives in PHP land Sonntag, 20. Februar 2011
  • 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 Adapter Sonntag, 20. Februar 2011
  • 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 tasks Sonntag, 20. Februar 2011
  • 5. Installing the conductor $ pear channel-discover pear.phing.info $ pear install phing/phing $ pear install -a phing/phing $ phing -v Sonntag, 20. Februar 2011
  • 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. 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. 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. 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. 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. How Phing empowers CI and CD Sonntag, 20. Februar 2011
  • 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 efficiency Sonntag, 20. Februar 2011
  • 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 flow Sonntag, 20. Februar 2011
  • 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. 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. 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. 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. 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 & commit Sonntag, 20. Februar 2011
  • 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 script Sonntag, 20. Februar 2011
  • 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. Using custom build listeners $ phing -logger phing.listener.BuildhawkLogger $ buildhawk --title 'Examplr' > examplr-build-times.html Sonntag, 20. Februar 2011
  • 22. Testing tasks and buildfiles Unit tests for custom tasks Structure and execution tests for the buildfile / build itself Sonntag, 20. Februar 2011
  • 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. 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. 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 doesn't contain a clean target"); } Sonntag, 20. Februar 2011
  • 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 doesn't depend on the clean target" ); } Sonntag, 20. Februar 2011
  • 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}' doesn't exist" ); } } Sonntag, 20. Februar 2011
  • 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. 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.collection Sonntag, 20. Februar 2011
  • 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. 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. 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. 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 standard Sonntag, 20. Februar 2011
  • 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. 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. 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. Brought to you by @raphaelstolt Sonntag, 20. Februar 2011
  • 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/1924450950 Sonntag, 20. Februar 2011
  • 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/phake Sonntag, 20. Februar 2011