Laravel Continuous Integration using Codeship

In my last blog post, I talked about how we test our Laravel applications. In this post, I’d like to expand on how we’ve set things up for Continuous Integration using a service called Codeship.

First of all, let me define the term. Wikipedia defines it as “In software engineering, continuous integration (CI) is the practice of merging all developer working copies to a shared mainline several times a day”. In practice, for us, it means that my team and I can create Pull Requests for our projects at any time, and our continuous integration will run on that branch and either give it a green flag, or point out any issues that might be happening.

We actually have two different CI services setup, Codeclimate, which we use for linting, coding standards and other code quality tools (I’ll have to blog about this later); and Codeship which we use to run all of our tests.

Part 1: setting up the world

The first thing you’ll do after signing up for a Codeship account is to connect it to your version control repository. For us that was Github, but they also support other providers. Next, you’ll be asked to configure your test pipeline(s). There’s two (or more if you have several test pipelines) steps to this. The first one is “Setup Commands” where you can setup the world. Basically, whenever Codeship runs the tests on your code, it’ll prepare a new fresh virtual machine with your latest code checked out on it. The setup commands are the commands that run immediately after that is done. Ours look like this:

# set php version
phpenv local 7.1
phpenv global 7.1
# node version
nvm install 7.6.0
# Install yarn
npm i -g yarn
# Install node-sass
yarn global add node-sass
# Optional GitHub auth for composer
composer config -g github-oauth.github.com [token]
# Install dependencies through Composer 
composer install --prefer-dist  --no-interaction
# set phpunit version
composer global require "phpunit/phpunit=5.*"
# copy env
cp -v .env.example .env
# generate key
php artisan key:generate
# front-end
yarn
npm rebuild node-sass
yarn build

I’ll break this down a bit:

  • First up, we setup the php and node versions that we want to use.
  • Secondly, I prefer working with yarn over npm as it is more reliable and locks dependency versions which prevents issues with version mismatches. So I install yarn. This part is optional if you prefer to use npm or don’t need to compile any front-end resources.
  • We use sass to write our css, and Codeship doesn’t come bundled with node-sass, so next up we install it.
  • Next, we optionally configure Composer’s GitHub auth with a token. This speeds things up a bit with composer installs, read more about it here. Then, we install Composer dependencies.
  • Next, we set the version of phpunit to be used, 5.x in our case at the time of writing.
  • Finally we do a bit of Laravel and project specific setup, copy our .env.example file, generate an application key, and run yarn to compile our front-end resources

Part 2: Unit & Feature tests

Screen Shot 2017-04-08 at 2.35.32 PM.png

By default, Codeship provides you with a single test pipeline, named “Test Commands”. We use this default one to run our Unit and Feature tests with phpunit.

The test command here is super simple:

php vendor/bin/phpunit

This just runs phpunit using the version that’s installed with composer locally for the project.

Part 3: Browser & JS tests

We use a second test pipeline to run our Browser (Dusk) tests and Javascript (jest) tests. This step does require a small change to Laravel’s default DuskTestCase to work correctly on Codeship.

We modified the driver() method in the tests/DuskTestCase.php file, like so:

/**
 * Create the RemoteWebDriver instance.
 *
 * @return \Facebook\WebDriver\Remote\RemoteWebDriver
 */
protected function driver()
{
    $chromeOptions = new ChromeOptions();
    if ($binary = env('DUSK_CHROME_BINARY')) {
        $chromeOptions->setBinary($binary);
    }
    $chromeOptions->addArguments(['no-first-run']);
    $capabilities = DesiredCapabilities::chrome();
    $capabilities->setCapability(ChromeOptions::CAPABILITY, $chromeOptions);

    return RemoteWebDriver::create(
        'http://localhost:9515',
        $capabilities
    );
}

Next, we create a separate .env.dusk.testing file for dusk specific environment configuration. In order to get Dusk running correctly on Codeship, you will need to specify the APP_URL as [http://lvh.me](https://documentation.codeship.com/basic/continuous-integration/using-subdomains/%5D so that Dusk has a URL to hit.

Finally, we can go back to the pipeline in Codeship and add the following commands:

# don't start chromedriver automatically
export DUSK_START_CHROMEDRIVER=false
export DUSK_APP_PORT=8000
# use correct version of chrome (57 )
ln -sf /usr/bin/google-chrome /home/rof/bin/chrome
export DUSK_CHROME_BINARY=/home/rof/bin/chrome
# startup chrome driver manually
nohup bash -c "./vendor/laravel/dusk/bin/chromedriver-linux 2>&1 &"
# boot up the larval app
nohup bash -c "php artisan serve --env=dusk.testing 2>&1 &" && sleep 4
# run dusk
php artisan dusk
# run our javascript tests
yarn test

First we setup some environment variables to overwrite the Dusk chrome driver auto-start, and set the port. Then we set the correct version of Chrome to use (by default Codeship uses chromium with an outdated version that doesn’t work with the latest webdriver…). Then we boot up the chromedriver, and the Laravel app using the built-in artisan php server.

Then we run our dusk tests, and finally our jest tests.

Putting it all together

As a last configuration step, we need to set environment variables using the Environment settings tab for the project in Codeship; this will set the correct settings to use for testing.

Screen Shot 2017-04-08 at 2.44.25 PM.png

These should hopefully be self-explanatory and are documented in the Laravel docs. We do also set the COMPOSER_HOME value so that Composer uses its internal cache to speed things up.

That’s it, from now on, whenever you push code to your repository, Codeship will run tests and notify you if any tests fail!

Screen Shot 2017-04-08 at 2.51.46 PM.png


Have you setup CI for your projects? Any struggles or tips you’d like to share? Let me know in the comments 💭👇.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s