A Path Towards Continuous Integration

Linux, Sever Administration, Technology

My first TravisCI build for AIP Demos site

I recently got PHPUnit and Laravel Dusk tests to pass for AIP Demos when either running Dusk in an OS with a graphical environment or one that is using xvfb (X virtual frame buffer). I also got them to run on the container created by TravisCI. It took quite a bit of trial and error, but that badge of an image above is a badge of honor. I had to slay quite a few more intimidating dragons than the pat on the back one deserves for launching, say, Atlassian sites or something.

The hardest part of these tests was getting Laravel Dusk to work in a pure command-line environment, namely the docker container I use to deploy it and the TravisCI container. Take a look at the docker container that AIP Demos is deployed on. It is based on the php:apache docker container that uses Debian version 8 (jessie). I had to add a few packages: xvfb is the virtualized X environment needed to allow a browser such as Chromium to be launched by the chromedriver command used by Laravel Dusk. libgconf-2-4 and libfontconfig are dependencies that chromium needs. And of course, you’ll want the chromium package. Some Linux distros, like Ubuntu, might have this as chromium-browser instead.

Next, I had to consult the repo of a savvy web server admin / developer to get things working from here. It seems that the chromedriver program will try to either start google-chrome or chromium-browser. For the php:apache container, installing google-chrome-stable from Google doesn’t seem to do the trick. Chromium has to be started with the --no-sandbox flag in order to properly accommodate chromedriver. A good way to do this is by symlinking the google-chrome and chromium-browser commands to a script, such as the one called xvfb-chromium created by this excellent user, which I only slightly customized. This can be done in a Dockerfile that does the following: copies the xvfb-chromium script to either /usr/bin or a less controversial place in the container server’s path, makes sure the script is executable, and creates the symlinks. I also decided to have Xvfb run on DISPLAY :0 instead of :99, which seemed to cause fewer problems. By far, the trickiest part was finding out how to make chromedriver, started by Dusk, launch the Chromium browser in a way in which Dusk was satisfied, and I have okaufmann to thank.

One caveat of this approach is that after php artisan dusk is ran, the chromium process is still left running. Since a DuskTestCase seems to only reset a database after the php artisan dusk command finishes, I needed to run several such commands for my various tests that required an empty DB, which results in several leftover chromium processes. I added a static tearDownAfterClass method to DuskTestCase that gets the PIDs of the chromium program and terminates them if the web app is running on Linux and /usr/bin/xvfb-chromium is present. tearDownAfterClass comes with PHPUnit and is ran after all the test methods in a class finish. It will also terminate the Xvfb process.

This setup finally got Laravel Dusk to successfully run tests in the docker container. The next challenge was making it work on TravisCI.

To make this happen, I ended up configuring the container with this .travis.yml, which basically came from one shared on laravel-news.com that should be in the docs and hopefully will be soon. The key was to use the Ubuntu “trusty” container with a modern version of PHP, making sure composer install is ran, starting xvfb from an init.d script that would appear to be available in this version of Ubuntu, running chromedriver manually in the background, manually copying .env.testing to .env, setting an appropriate DISPLAY environment variable, and starting the built-in PHP web server. Another crucial thing to remember is to set APP_URL in .env.testing to a URL that the web server will run on, which is typically http://localhost:8000.

These are the secrets to my success. I hope you might find this experience helpful.

What say you?