Symfony2 Continuous Integration with Jenkins, Ant, and Capistrano

At iostudio we work on many different projects. One of the ways we try to make
sure that our quality of code is high is to use
[Jenkins](http://jenkins-ci.org/) to make sure all of our projects will build
successfully and be able to deploy.

All of our new work gets is built using the [Symfony2](http://symfony.com)
framework. One advantage that we have found is that we are able to create our
own iostudio [Symfony Distribution](http://symfony.com/distributions) that
contains all of our build scripts and deployment scripts within. Now let’s get
down to business and into some code.

The directory structure.

![tree -L 1](http://media.tumblr.com/tumblr_mai1ju4p9U1rtbdeo.png)

If you are familiar with the [Symfony2 Directory
Structure](http://symfony.com/doc/master/book/page_creation.html#the-directory-structure)
You will notice a few things different. You see some new files (build.xml,
Capfile) and directories (build/, config/).

The build.xml is an [Ant](http://ant.apache.org/) build file that tells ant
that it needs to clean up a few directories and get a few things ready before
we start to run some build tests on the project. All the files related to this
are kept in the build directory.

The Capfile and the config folder are the basis for our deployments using
[Capifony](http://capifony.org/) with a
[Multistage](https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension)
deployment.

The README.md file contains notes related to the project such as getting the
project setup and running such as setting up the database and creating users.
(Note: Some of our projects include a bash script that does all of this for
you).

Before I go too much further I also want to stop for a second and talk about
our Framework Bundle. We have our own customized framework bundle defined in
composer.json that allows us to include cli commands to things such as create
controllers, generate an admin backend, and check to see if the user coming to
the site is using on a mobile platform or not.

Let’s pick apart the build.xml file now.

The build file is responsible for cleaning up after old builds, getting things
ready for the new build, and running all the tests to make sure everything is
good. We also put each task in it’s own file and have a task that we can use to
updated those files to make sure everything stays in check.

First we need to clean up after old builds.

{% highlight xml %}







{% endhighlight %}

Now clean up symfony2 related stuff

{% highlight xml %}



















































{% endhighlight %}

> **NOTE** notice the line where it copies the parameters.yml.dist to
> parameters.yml. You database setting for the build server will be located in
> this file.

Now that we have everything built, let’s start with doing some lint checks on the php and twig files.

{% highlight xml %}


















{% endhighlight %}

[phploc](https://github.com/sebastianbergmann/phploc)

{% highlight xml %}










{% endhighlight %}

[pdepend](http://pdepend.org/)

{% highlight xml %}








{% endhighlight %}

[phpcpd](https://github.com/sebastianbergmann/phpcpd)

{% highlight xml %}










{% endhighlight %}

[phpcs](http://pear.php.net/package/PHP_CodeSniffer)

{% highlight xml %}








{% endhighlight %}

We use the [psr](https://github.com/php-fig/fig-standards) standards here.

[phpmd](http://phpmd.org/)

{% highlight xml %}








{% endhighlight %}

[phpunit](https://github.com/sebastianbergmann/phpunit/)

{% highlight xml %}





{% endhighlight %}

So there are most of the build scripts. Each are in it’s own file to allow us
to run a quick script to update all of these. Now Jenkins needs to be setup. We
have our own symfony2 jenkins template that we use that includes all the tools
that we use to read the output of all the tests above. We also have some
documentation auto generated for us using phpdoc. I have left this script out
and you can insert your favorite php documentor for that.

There are various tutorials on setting up Jenkins so I will skip that and get
into deploying with Capifony. The plugin that we use is the [Post Build
Task](https://wiki.jenkins-ci.org/display/JENKINS/Post+build+task) which only
runs when everything else is within an acceptable range. ie None of the phpunit
tests failed.

Let’s now go over the Capifony configs. For each deployment we only want to
share the logs, the uploads, and the vendor directories. We don’t share the
parameters.yml file.

The reason why the parameters.yml file is not shared is because for each
project we maintain a parameters.yml.stage. Stage refers to the capistrano
multistage extension. For every project we have at least a beta server and a
production server. The configuration for this is setup in the specific files.

Allow me to rant for a moment on this. The reason why this are setup like this
is so we can look at the parameters.yml.beta file and see how it’s setup. Most
of the time we use a throw away Google Analytics account on the beta server to
avoid beta tests getting into production analytic logs. It’s also to avoid
having to ssh into the servers and see what is configured there.

Once the deployment takes place, we hook into the `deploy:finalize_update` task
and do two things. First we have a robots.txt.beta that we copy over to the web
folder that tells search engines to keep the beta site out of it’s indexes. The
other is to copy over the parameters.yml.beta file.

{% highlight ruby %}
# app/config/deploy.rb
# … snip …
before(“deploy:finalize_update”) do
run “if [ -f #{current_release}/web/robots.txt.#{stage} ]; then cp #{current_release}/web/robots.txt.#{stage} #{current_release}/web/robots.txt; fi”
run “if [ -f #{current_release}/app/config/parameters.yml.#{stage} ]; then cp #{current_release}/app/config/parameters.yml.#{stage} #{current_release}/app/config/parameters.yml; fi”
end
# … snip …
{% endhighlight %}

It takes awhile to get all of this setup for each of our clients, but we have
found that deploying code reduces the amount of people asking for us to deploy
code up for our QA department or for client approval.