Continuous Integration of PHP Project Using Shippable

Recently I started using Shippable as my continuous integration service. It adds a lot to peace of mind knowing that your tests are always run (even if you forget to do it on your local environment) and you have another point of failure for your application.

The biggest reason I chose Shippable is that it has a sane pricing. You get one concurrent build for free and this includes the private repositories. This works great for side projects which you don't want to make public, but want to use good tools without a lot of hassle. If the project picks up and you need more hamster power it's easy to ramp it up without having to sell a kidney or two. For those wanting to say that "We need to charge that much to provide good service" I understand that and I personally look with one eyebrow raised at software which is just free (business needs to make money to survive). Just that for simple side projects (hobby) that will probably won't bring even a dollar spending hundreds a month is not a best financial decision.

You can jump to any of the sections:

Prerequisites

To start using this service you just need to sign up using your GitHub or BitBucket account. If you want to test private repositories you will have to give Shippable permission to them (by default it only sees public repositories). The code used in this article is available at GitHub. It's a Symfony project, but with small changes any PHP project could be tested.

Initial configuration

Your project needs to have a shippable.yml configuration file. In it you define scripts which will be run, environment variables, language, versions to test, etc.

First let's configure Shippable to test a PHP project and do it for 5.5 & 5.6 versions. Add the following to your shippable.yml file:

language: php

php:
    - 5.5
    - 5.6

Just in case it might be helpful to have the application log files (in case the build is failing and you're not sure why). You can tell shippable to save the artifacts from a build and make it available for download using the archive option. The shippable directory is a special one were all the files from build is being kept (like test coverage reports, etc.). Add the following to your shippable.yml file:

archive: true

after_script:
    - cp -r ./app/logs shippable/logs

This will create an archive for download after the build is done. The after_script is commands which are run after the build and they will not affect the status of the build.

To have our project working we need to install libraries using composer. First we update composer to have the latest version and then install all the required libraries:

before_script:
      - composer self-update -n
      - composer install --prefer-dist -n

PHPUnit testing

The AppBundle which comes with new Symfony project has a simple PHPUnit test. Add the following to run the PHPUnit tests:

script:
    - phpunit -c app --log-junit shippable/testresults/junit.xml --coverage-xml shippable/codecoverage

Commit the changes and push it to GitHub. Now that we have the basic set-up for Shippable it's time to add it to a service.

Enable project in Shippable

First choose the subscription to use and when in projects section click the big plus icon which says "Enable Project."

shippable-choose-subscription

Force a synchronization if the repository you want is not in the list. Click on key icon to enable the project.

shippable-enable-project

After it is done you can manually start a build or it will happen automatically the next time you will push a commit.

After a while a build should finish and you should get the similar view.

shippable-phpunit

By clicking "Download Artifacts" you can download the logs & other files which were generated during the build.

phpspec testing

Lets expand our testing suite with phpspec. You can see that code was added in this pull request.

To run phpspec tests just add a command to script in your shippable.yml file:

script:
    # ...
    - ./bin/phpspec run -fpretty --verbose -n

You should be able to see similar view if everything was correct.

shippable-phpspec

Behat testing

Lets expand our testing suite with Behat. You can see that code was added in this pull request. Please note that currently there is no tagged version of Behat (at the time of writing latest is 3.0.15) which has junit formatter, for that reason a dev version is installed.

To run Behat tests we will start the built-in web server & then run the tests. This can be done by adding following commands:

script:
    # ...
    - ./app/console server:start -n
    - ./bin/behat --format=progress --out=std --format=junit --out=shippable/testresults -n

After the tests are finished, stop the built-in web server:

after_script:
    - ./app/console server:stop -n

The default XDebug nesting level is set at 100 which is very small and throws a fatal PHP error. This can be fixed by setting it at higher level in php.ini file. Add the following to your shippable.yml file:

before_install:
    - echo "xdebug.max_nesting_level = 1000" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini

You should be able to see similar view if everything was correct.

shippable-behat

Environment variables & MySQL

You will probably be using a MySQL or something similar for the persistence in your application. It's very easy to include services with Shippable.

You don't want to hard code configuration variables for your application. To fix that we will use the environment variables to configure the application depending on the environment it is in. You can set the global environment variables which are the same between builds and you can set simple environment variables which are different between builds (extending your testing matrix). If you have environment variables which you don't want to show (like passwords) you can add it as a secure environment variable.

You can see the code which was added in this pull request.

To get the secure environment variable you need to go to project settings. It has a panel "Encrypt Env Vars." You provide the environment variables in the box and after clicking "Encrypt" you get the snippet of code you can paste in your shippable.yml file.

shippable-secure-environment-variable

Environment variables which start with SYMFONY__ are converted to normal variables after composer finishes the install. The double underscore becomes a dot. The environment variable SYMFONY__ENV__DATABASE_NAME becomes env.database_name parameter in your application. Lets add the database configuration and a secret value:

env:
    global:
        - SYMFONY__ENV__DATABASE_HOST=127.0.0.1
        - SYMFONY__ENV__DATABASE_PORT=~
        - SYMFONY__ENV__DATABASE_NAME=symfony
        - SYMFONY__ENV__DATABASE_USER=shippable
        - SYMFONY__ENV__DATABASE_PASSWORD=
        # Secret parameter value
        - secure: L9Ws6Wcx1XVoJYAP/YINXd4tGJwDsdG1yj7M/dLtvhZBhS2oLUvtxir7MGWDpbV69Qc+/FWUgUchpd4NUNHqEwc80YUFGLxylJ5Z82Cqnp9cf1qoM+gtHAM6J6o4TY7/jnuYsjvK0f++G+PJKiTYaky9QvK1qb+ERRxfk2AqoM7p7J3PYQz05lIKwuGmmmONfvlelT4Sv+8RPgfWSbJ1RBEN9zPS4Psh23QZ4T/zThQwyiNDwCv0QcUyQ7Mm+SFo59Eh34901DJwttZKYXG8ruO9iJs+ow7cAMAAAZ7sqgx7PWWIodMfpUjAlut45m/AMDi3AsAeG42262/o/9hRkA==

The MySQL database can be created by adding the command in before_script option:

before_script:
    # ...
    - mysql -e "CREATE DATABASE $SYMFONY__ENV__DATABASE_NAME"
    - ./app/console doctrine:schema:create -n

This will create the database as it's defined in the environment variable. After the database is created it will run the queries to create a schema for your application.

We're using built-in server for testing our application. However it has no access to environment variables. To fix that extend your php.ini configuration to include them (you probably won't need this if you're using Apache or different web server):

before_install:
    # ...
    - echo 'variables_order = "EGPCS"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini

You should be able to see similar view if everything was correct.

shippable-environment-variables-mysql

Update 2016-04-27: In the new build systems MySQL is not started automatically. If you get an error ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2), enable MySQL in Shippable configuration file:

services:
    - mysql

Conclusion

With these small changes you should have a CI testing for your application. It will improve your code quality and give you a piece of mind knowing that the changes you made will be tested even if you forgot to run the tests on your development machine.

Posted in: CI, PHP, Symfony, Technical, Testing

Comments