{"id":519,"date":"2017-09-16T13:00:45","date_gmt":"2017-09-16T12:00:45","guid":{"rendered":"http:\/\/chewett.co.uk\/blog\/?p=519"},"modified":"2017-09-10T21:05:46","modified_gmt":"2017-09-10T20:05:46","slug":"configuring-teamcity-php-project","status":"publish","type":"post","link":"https:\/\/chewett.co.uk\/blog\/519\/configuring-teamcity-php-project\/","title":{"rendered":"Configuring TeamCity for a PHP project"},"content":{"rendered":"<p>This post describes configuring TeamCity for a PHP project using composer. Setting up continuous integration of unit testing from a git repository.<\/p>\n<p><!--more--><\/p>\n<h2>Setting up PHP, composer and git<\/h2>\n<p>The first step to running your PHP project on TeamCity is installing PHP and the appropriate\u00a0libraries. The only requirement for this is that PHP is installed somewhere that TeamCity is able to access. It does not need to be set in your path.<\/p>\n<p>In addition, since PHP doesn&#8217;t need to be in your path it means that you can have multiple PHP versions installed. I will be installing PHP 5.6 and 7.1 to run continuous integration concurrently on both versions.<\/p>\n<p>Once PHP has been installed you will need to install composer. Depending on your operating system you can either install a windows binary or run a composer install shell script. Full details are available on <a href=\"https:\/\/getcomposer.org\/doc\/00-intro.md\">the composer Intro document<\/a>.<\/p>\n<p>Since I am using a git based version management system I had to install git. To do this you either need to install it via your package manager (apt-get\/dnf\/yum install git) or for windows you can install the binary. The git website offers multiple ways to <a href=\"https:\/\/git-scm.com\/downloads\">download git<\/a>\u00a0one of which should work for your operating system.<\/p>\n<h2>Setting up a VCS Root with Git<\/h2>\n<p>I am going to configure TeamCity to run continuous integration on all branches in a git repository. The fetch URL of the VCS root is the URL that git will use to load the repository. This can be any method that git clone supports including https and SSH.<\/p>\n<p>The default branch is the primary branch that this VCS root will be configured on. TeamCity will default to using this branch. Additional branches can be configured by using the &#8220;branch specification&#8221; field. To monitor all branches you can use a wildcard.<\/p>\n<pre>+:refs\/heads\/*<\/pre>\n<p>The above branch specification rule will add all branches in the git repository.<\/p>\n<p>Depending on the fetch URL you may need to add authentication to the fetch method. This allows both password and private key authentication for authenticating to your repository.<\/p>\n<h2>Configuring Build Commands for Composer and PHPunit<\/h2>\n<p>For a composer based project you will need to run <code>composer install<\/code>\u00a0to download all required assets. This should be the first build step. This can be added as a <code>command line<\/code>\u00a0build step with the custom script value of <code>composer install<\/code>.\u00a0Once this first build step has finished all required assets should be installed and your project should be prepared.<\/p>\n<p>Now we can configure PHPUnit to run all the unit tests in the project. \u00a0Again we create this as a custom command line build step. Here we set the command executable to <code>php<\/code>\u00a0and the parameter to point to phpunit.<\/p>\n<pre>Command executable: php\r\nCommand parameters: vendor\\phpunit\\phpunit\\phpunit -c build\/phpunit.xml<\/pre>\n<p>If you are going to run multiple PHP versions the path to your command executable could be a build variable. Using something like <code>%phpExe%<\/code>\u00a0and then defining this variable for each build will let you easily configure which php version you want to build with.<\/p>\n<p>Above we point to the <code>phpunit<\/code>\u00a0file in the composer vendor folder and our phpunit xml file. This phpunit file defines where the tests live and how to run them. My example phpunit\u00a0file includes:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\r\n&lt;phpunit backupGlobals=&quot;false&quot;\r\n         backupStaticAttributes=&quot;false&quot;\r\n         colors=&quot;true&quot;\r\n         convertErrorsToExceptions=&quot;false&quot;\r\n         convertNoticesToExceptions=&quot;false&quot;\r\n         convertWarningsToExceptions=&quot;false&quot;\r\n         bootstrap=&quot;..\/vendor\/autoload.php&quot;\r\n&gt;\r\n    &lt;testsuites&gt;\r\n        &lt;testsuite name=&quot;main&quot;&gt;\r\n            &lt;directory&gt;..\/src&lt;\/directory&gt;\r\n            &lt;directory&gt;..\/tests&lt;\/directory&gt;\r\n        &lt;\/testsuite&gt;\r\n    &lt;\/testsuites&gt;\r\n    &lt;filter&gt;\r\n        &lt;whitelist&gt;\r\n            &lt;directory&gt;..\/src&lt;\/directory&gt;\r\n            &lt;directory&gt;..\/tests&lt;\/directory&gt;\r\n        &lt;\/whitelist&gt;\r\n    &lt;\/filter&gt;\r\n    &lt;logging&gt;\r\n        &lt;log type=&quot;coverage-html&quot; target=&quot;coverage&quot;\/&gt;\r\n        &lt;log type=&quot;coverage-clover&quot; target=&quot;logs\/clover.xml&quot;\/&gt;\r\n        &lt;log type=&quot;coverage-crap4j&quot; target=&quot;logs\/crap4j.xml&quot;\/&gt;\r\n        &lt;log type=&quot;junit&quot; target=&quot;logs\/junit.xml&quot; logIncompleteSkipped=&quot;false&quot;\/&gt;\r\n    &lt;\/logging&gt;\r\n&lt;\/phpunit&gt;<\/pre>\n<p>This tells PHPUnit where the source code, and test locations and also defines some additional coverage data to be logged. The composer autoloader file is specified by setting the bootstrap variable to the vendor autoload file.<\/p>\n<h2>Generating Artifacts<\/h2>\n<p>The final step once we have PHPUnit producing coverage information is to package these as build artefacts. These are taken from the working directory after running all the build steps and saved. Using my PHPUnit XML file combined with the following lines added to the Artifacts Path text box we are able to save our coverage artefacts.<\/p>\n<pre>build\/coverage =&gt; coverage.zip\r\nbuild\/logs\/junit.xml =&gt; junit.xml\r\nbuild\/logs\/clover.xml =&gt; clover.xml<\/pre>\n<p>Here this packages the coverage folder as a coverage zip file. This was produced by PHPUnit and is a browseable set of HTML files showing test coverage. TeamCity lets you view this zip file without downloading it and means you can easily review coverage.<\/p>\n<p>The other XML files allow easy importing of unit test coverage data into your IDE of choice.<\/p>\n<h2>Running your Tests<\/h2>\n<p>Now your\u00a0unit tests are set up so that whenever a new commit is pushed into your repository the unit tests will run. In addition, coverage will be analysed and a HTML report is created. This is an important step in ensuring new code doesn&#8217;t regress older feature and therefore, ensuring a projects stability.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post describes configuring TeamCity for a PHP project using composer. Setting up continuous integration of unit testing from a git repository.<\/p>\n","protected":false},"author":1,"featured_media":649,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[5],"tags":[168,66,169,170],"class_list":["post-519","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-informational","tag-composer","tag-php","tag-teamcity","tag-unit-testing"],"wppr_data":{"cwp_meta_box_check":"No"},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2017\/09\/configuring_php_unit_testing_with_teamcity.jpg?fit=800%2C800&ssl=1","jetpack_shortlink":"https:\/\/wp.me\/p2toWX-8n","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":3073,"url":"https:\/\/chewett.co.uk\/blog\/3073\/celebrating-30-years-of-php-highlights-from-jetbrains-phpverse-2025\/","url_meta":{"origin":519,"position":0},"title":"Celebrating 30 Years of PHP: Highlights from Jetbrains PHPVerse 2025","author":"Chewett","date":"July 6, 2025","format":false,"excerpt":"This year I (virtually) attended Jetbrains PHPVerse 2025 celebrating 30 years of PHP. I have written about two sessions which most interested me. 30 Years of PHP To celebrate 30 years of PHP, Jetbrains brought together a number of speakers to talk about PHP's past successes and the future of\u2026","rel":"","context":"In &quot;Informational&quot;","block_context":{"text":"Informational","link":"https:\/\/chewett.co.uk\/blog\/category\/informational\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2025\/07\/phpverse-4.webp?fit=1200%2C675&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2025\/07\/phpverse-4.webp?fit=1200%2C675&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2025\/07\/phpverse-4.webp?fit=1200%2C675&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2025\/07\/phpverse-4.webp?fit=1200%2C675&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2025\/07\/phpverse-4.webp?fit=1200%2C675&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":148,"url":"https:\/\/chewett.co.uk\/blog\/148\/location-geoip-dat-php\/","url_meta":{"origin":519,"position":1},"title":"Location of GeoIP.dat for PHP","author":"Chewett","date":"September 1, 2013","format":false,"excerpt":"To find the location of GeoIP for PHP you can ssh into the server and use the \"find\" command. find \/ -iname GeoIP.dat Will return a list of file paths where the file with this name is found. In my case I found: \/usr\/share\/GeoIP\/GeoIP.dat \/usr\/local\/share\/GeoIP\/GeoIP.dat \/opt\/geoip\/share\/GeoIP\/GeoIP.dat In my case the\u2026","rel":"","context":"In &quot;Informational&quot;","block_context":{"text":"Informational","link":"https:\/\/chewett.co.uk\/blog\/category\/informational\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2409,"url":"https:\/\/chewett.co.uk\/blog\/2409\/how-to-install-teamcity-as-a-service-on-windows\/","url_meta":{"origin":519,"position":2},"title":"How to install TeamCity as a Service on Windows","author":"Chewett","date":"March 14, 2020","format":false,"excerpt":"Today I have written a small note on how to install the Jetbrains TeamCity service on windows. Installing TeamCity as a service on Windows To install TeamCity as a service you will need to find the location of your TeamCity install. For me I have installed it under: C:\\Teamcity\\ Once\u2026","rel":"","context":"In &quot;Informational&quot;","block_context":{"text":"Informational","link":"https:\/\/chewett.co.uk\/blog\/category\/informational\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2020\/02\/install_teamcity_service_icon.jpg?fit=800%2C800&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2020\/02\/install_teamcity_service_icon.jpg?fit=800%2C800&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2020\/02\/install_teamcity_service_icon.jpg?fit=800%2C800&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2020\/02\/install_teamcity_service_icon.jpg?fit=800%2C800&ssl=1&resize=700%2C400 2x"},"classes":[]},{"id":1226,"url":"https:\/\/chewett.co.uk\/blog\/1226\/changing-boot-order-with-grub-on-fedora\/","url_meta":{"origin":519,"position":3},"title":"Changing boot order with GRUB on Fedora","author":"Chewett","date":"June 6, 2018","format":false,"excerpt":"In this post I talk about how you can change the default selected OS and reorder the boot list in GRUB for Fedora. The Default GRUB ordering By default when the GRUB bootloader is installed it will search for all installed operating systems. Their default ordering is based on the\u2026","rel":"","context":"In &quot;Fixes&quot;","block_context":{"text":"Fixes","link":"https:\/\/chewett.co.uk\/blog\/category\/fixes\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/06\/altering_boot_order_grub_fedora.jpg?fit=800%2C800&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/06\/altering_boot_order_grub_fedora.jpg?fit=800%2C800&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/06\/altering_boot_order_grub_fedora.jpg?fit=800%2C800&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/06\/altering_boot_order_grub_fedora.jpg?fit=800%2C800&ssl=1&resize=700%2C400 2x"},"classes":[]},{"id":375,"url":"https:\/\/chewett.co.uk\/blog\/375\/setting-up-the-ci20-for-the-cluster\/","url_meta":{"origin":519,"position":4},"title":"Setting up the CI20 for the cluster","author":"Chewett","date":"October 27, 2018","format":false,"excerpt":"This post talks about the steps I have followed to set up my Creator CI20 for the Raspberry Pi Cluster. Burning Debian to the onboard NAND and configuring it To run the CI20 on the Raspberry Pi Cluster I am going to write Debian 8 to the NAND storage. First\u2026","rel":"","context":"In &quot;Raspberry Pi Cluster&quot;","block_context":{"text":"Raspberry Pi Cluster","link":"https:\/\/chewett.co.uk\/blog\/category\/raspberry-pi-cluster\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/10\/ci40_setup.jpg?fit=800%2C800&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/10\/ci40_setup.jpg?fit=800%2C800&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/10\/ci40_setup.jpg?fit=800%2C800&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2018\/10\/ci40_setup.jpg?fit=800%2C800&ssl=1&resize=700%2C400 2x"},"classes":[]},{"id":831,"url":"https:\/\/chewett.co.uk\/blog\/831\/enabling-ssh-ubuntu-16-04\/","url_meta":{"origin":519,"position":5},"title":"Enabling SSH on Ubuntu 16.04","author":"Chewett","date":"November 8, 2017","format":false,"excerpt":"By default you are not able to SSH into an Ubuntu 16.04\u00a0 machine and this blog post describes the steps needed to install SSH server. Installing OpenSSH Server To enable logging in from another computer via SSH you need to install a SSH\u00a0server. To do this you\u00a0can run the following\u2026","rel":"","context":"In &quot;Informational&quot;","block_context":{"text":"Informational","link":"https:\/\/chewett.co.uk\/blog\/category\/informational\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2017\/11\/enable_ssh_on_ubuntu.jpg?fit=800%2C800&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2017\/11\/enable_ssh_on_ubuntu.jpg?fit=800%2C800&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2017\/11\/enable_ssh_on_ubuntu.jpg?fit=800%2C800&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/chewett.co.uk\/blog\/wp-content\/uploads\/2017\/11\/enable_ssh_on_ubuntu.jpg?fit=800%2C800&ssl=1&resize=700%2C400 2x"},"classes":[]}],"_links":{"self":[{"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/posts\/519","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/comments?post=519"}],"version-history":[{"count":8,"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/posts\/519\/revisions"}],"predecessor-version":[{"id":650,"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/posts\/519\/revisions\/650"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/media\/649"}],"wp:attachment":[{"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/media?parent=519"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/categories?post=519"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/chewett.co.uk\/blog\/wp-json\/wp\/v2\/tags?post=519"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}