Scott Robinson Technical Lead

Running PHPUnit in Isolated Environments with Ubuntu, Docker, Gitlab & Gitlab CI

I’m currently working on a new project that houses a framework for multi-tenant applications based on Laravel. The source code is stored in a hosted Gitlab instance via Githost.io – which is run by Gitlab themselves. There’s a set of unit tests that I wanted to run via continuous integration to run the tests with each commit and fast.

Throughout this post, the Docker daemon is running at tcp://192.168.1.254:4243/ instead of via a socket. This is because I use the API for other things over HTTP.

The local development environment is handled by Vagrant, using a custom box based upon Ubuntu that includes Composer, MariaDB and a few other components. This works very well, one simple Vagrantfile can be dropped in to new projects and immediately get running with a LAMP style stack:

Vagrant.configure("2") do |config|
    config.vm.provider :virtualbox do |v|
        v.name = "mult"
        v.customize ["modifyvm", :id, "--memory", 2048]
        config.vm.box = "ssx/lamp"
        config.vm.network :private_network, ip: "192.168.13.37"
        config.ssh.forward_agent = true
    end

    config.vm.synced_folder "./", "/vagrant", type: "nfs"
end

This box works quite well, but to Vagrant up and run tests using this with every commit would be quite slow. Enter Docker. I converted the image that was used in the Vagrant environment to a Docker one and now tests can be ran within seconds instead of minutes.

Gitlab CI has a Docker mode of operation. I couldn’t quite seem to get that up and running well. You can find the instructions for installing a Gitlab CI runner on this page.

I went via the shell route and writing the Docker commands manually to boot up in container and then run our script and tests, in the repo I have build.sh which must be set to +x to execute:

#!/usr/bin/env bash

# Restart the services within the container
service mysql restart
service nginx restart
service php5-fpm restart

# Move into the directory
cd /vagrant

# Run our migrations
php artisan migrate
php artisan db:seed

# Execute our test suite
vendor/bin/phpunit

Next, the .gitlab-ci.yml file needs to be created to run the build and then tests:

stages:
  - test

job1:
    stage: test
    script: 
     - composer install
     - cp .env.example .env
     - php artisan key:generate
     - docker run -d -t -v `pwd`:/vagrant hellossx/lamp:1.0.4 > container.pid
     - docker exec `cat container.pid` /vagrant/build.sh
    tags:
     - docker

This does mean that if tests fail, the container will never get cleaned up. You can add a cronjob to clean up these orphaned containers, with something like this docker-clear-old.sh script:

#!/bin/bash

# Set our docker host
export DOCKER_HOST="tcp://192.168.1.254:4243/"

# Clean up old images that failed tests
for i in $(docker ps -a | grep "hour ago" | cut -f1 -d" "); do docker stop $i && docker rm $i; done

This will remove the containers after an hour and leave the previous ones around for a short while incase you want to attach and investigate anything.

Found this useful? Everything here is free and stays that way. If it saved you an afternoon, you can buy me a coffee.