Continuous Delivery with Symfony + CircleCI + Capistrano

Tom Newby
4 min readFeb 7, 2016

I’m a big fan of continuous delivery — if you’ve got something ready to release (i.e. you’ve accepted the pull request) then the best time to deploy is right now.

I hadn’t been able to find a whole lot about actually implementing continuous delivery with Symfony, so I’ve pieced one together for PiggyBike and figured I’d share it with others.

PiggyBike is a fairly straightforward Symfony app for now. It runs on a single DigitalOcean instance provisioned with PuPHPet, with PHP7, nginx, MySQL and Redis. So far, I’ve been deploying using Capifony, a collection of recipes for deploying Symfony apps with Capistrano. I’ve used it a lot in the past, and while there’s more preferable ways to deploy (blue/green builds with containers etc.), given the single instance nature of this application, it’s incredibly reliable.

To orchestrate the whole operation of CI/CD, I’ve opted to utilise CircleCI. After weighing up the values of their competitors, I settled on CircleCI as I found the config flexible and easy enough (I do love me a good YAML config).

So, here’s my circle.yml:

machine:
php:
version:
7.0.0RC7
ruby:
version:
2.0.0-p481

dependencies:
pre:
- cp $HOME/$CIRCLE_PROJECT_REPONAME/app/config/parameters.yaml.circle.dist $HOME/$CIRCLE_PROJECT_REPONAME/app/config/parameters.yml
post:
- rvm use 2.0.0-p481 do gem install psych
- gem install capifony
test:
override:
- SYMFONY_DEPRECATIONS_HELPER="weak" phpunit -c app/

deployment:
production:
branch:
production
commands:
- cap -S revision=$CIRCLE_SHA1 deploy

Out of the box, CircleCI is pretty intelligent at learning what you do, but often projects will have some nuances. Here are mine!

Setting up the testing machine

I configured the machine for PHP7 and manually specified the Ruby version, for Capifony. To be able to run the tests, I need to actually create a test instance of the app, so I need to be able to utilise a database. Luckily, CircleCI already provides one (user: ubuntu, name: circle_test, no password), so I create a parameters.yml.circle.dist in my app/config directory so that I can copy this to parameters.yml before composer install has run. Here’s a cut of my parameters.yml.circle.dist:

parameters:
database_host: 127.0.0.1
database_port: ~
database_name: circle_test
database_user: ubuntu
database_password: ~
...

After the dependencies have been resolved (this involves installing PHP, Ruby etc.) I had to manually install the psych Rubygem, as I was getting some issues installing Capifony. I’m not really a Ruby user at all, so I have no idea what the problem really was, but that fixed it.

Running the tests

Running the tests is pretty easy — CircleCI preinstalls PHPUnit so it’s available on your $PATH. However you’ll probably run into the issue where the PHPUnit Bridge module from Symfony converts deprecation notices into big red flags in PHPUnit which causes it to exit with a non-zero exit code, which breaks the build.

This is an awesome feature, especially if you’re starting a new project that you want to prevent any deprecated calls making into production — but in my opinion, it doesn’t exactly break the build. Luckily you can pass the environment variable SYMFONY_DEPRECATIONS_HELPER set to “weak” which will still report the warnings, but prevent PHPUnit from throwing a non-zero exit code, which will allow your build to progress to the next stage, deployment!

Deploying with CircleCI + Capistrano

I wanted to setup automated deployments on a single branch “production” so that I could just merge master into this branch when I’m ready to deploy (usually a few times per night).

CircleCI lets you achieve this with the YML file by specifying different deploy strategies per branch — you could configure this to deploy master to a staging environment on every push if you wanted.

deployment:
production:
branch:
production
commands:
- cap -S revision=$CIRCLE_SHA1 deploy

The commands key allows you to specify a list of command to invoke your deployment, so it’s really powerful to modify it how you want — but I just want to run cap deploy.

The default behaviour of cap deploy is to simply checkout the latest branch you specify in your deploy.rb. There is however an option to pass in a specific Git hash to be deployed and there is a swathe of environment variables available inside the CircleCI container — you can see them all in the docs.

You’ll need to make sure your deploy.rb is configured properly to be able to connect to the remote server, which will involve you adding an SSH keypair for the CircleCI container. I did this by jumping into the container via SSH (there’s a “Restart with SSH” option) and running ssh-keygen and adding it to the “SSH Permissions” section of the Project Settings.

I’ll put together a better guide for how to handle database changes soon and when PuPHPet allows for multi-server support, I’ll be aiming to go to a two-server setup with green/blue style deploy and will write about that too.

If you have any questions, you’re welcome to hit me up on Twitter: @tomnewbyau

If you’d like to try out DigitalOcean, you can use my referral link which earns me a measly $25 when you spend $25. Alternatively, you can tweet me @tomnewbyau and I’ll invite you which will give you $10 credit (that’s 2 months of the smallest server!)

--

--