Test and Deploy Laravel with Chipper CI

July 21st, 2022

Chipper CI is continuous integration, just for Laravel. Give it a try - free!

Chipper CI was designed to make it quick and easy to test and deploy your Laravel applications.

We're going to see how to setup a Laravel application, add some tests, and then use Chipper CI to test and deploy it.

The whole process just takes a few minutes.

A Quick Laravel App

If you're not starting from zero - great. You'll have most of this done already. However, if you want to follow along and spin up a new application to try Chipper CI out, here's how!

# Create a new Laravel application in directory my-new-app
composer create-project laravel/laravel my-new-app

cd my-new-app

Straight away, we can actually run some tests:

./vendor/bin/phpunit

But if you wanted to create some new tests, you can! Let's create a new controller, and have it bootstrap a test for that controller as well:

php artisan make:controller --test MyFirstController

We'll setup a route that uses the controller, and have the controller respond with a request.

# File routes/web.php
Route::get('/my-route', [App\Http\Controllers\MyFirstController::class, 'respond'])

# File app/Http/Controllers/MyFirstController.php
class MyFirstController extends Controller
{
    public function respond()
    {
        return "my response";
    }
}

We created a new route, and assigned the handler for that route to the MyFirstController class's respond method. This just responds with the text “my response”.

Let's test this!

When we created the controller, we had it create a test. We can find this test in tests/Feature/Http/Controllers/MyFirstControllerTest.php.

<?php

namespace Tests\Feature\Http\Controllers;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class MyFirstControllerTest extends TestCase
{
    public function test_we_get_a_response()
    {
        $response = $this->get('/my-route');

        $response->assertStatus(200);
        $response->assertSee("my response");
    }
}

If all goes well, we should be able to run our tests and see this new test is passing!

# Run the tests
./vendor/bin/phpunit

# Output will be something like this:
PHPUnit 9.5.21 #StandWithUkraine

...                                                                 3 / 3 (100%)

Time: 00:00.070, Memory: 22.00 MB

OK (3 tests, 4 assertions)

Now that we have an application and some tests, we can setup a project in Chipper CI, run our tests, and see how we might deploy it.

Testing in Continuous Integration

Setting a project up in Chipper CI involves logging in, clicking to create a new project, and authenticating again GitHub, GitLab, or BitBucket. Once authenticated, you'll see a list of repositories to choose from, and you can create select whichever you'd like!

chipper create project

Once the project is created in Chipper CI, you'll see instructions on adding a .vessel.yml file. It's pretty simple!

chipper ci yaml file

You can copy/paste that Yaml, and create a new file in your repository. Once you push that file up, you should quickly see a build started for that repository!

cat <<YAML >> .chipperci.yml
version: 1

environment:
  php: 8.1
  node: 16

pipeline:
  - name: Setup
    cmd: |
      cp -v .env.example .env
      composer install --no-interaction --prefer-dist --optimize-autoloader
      php artisan key:generate

  - name: Run Tests
    cmd: phpunit
YAML

git add .
git commit -m "added Chipper CI yaml file"
git push origin main

The Chipper CI Yaml file controls what happens in your build. You can define the PHP/Node version, additional services (MySQL, PostreSQL, Redis, Docker, and more), setup build triggers, and more!

Any service setup (such as MySQL or Redis) will automatically add the correct values for Laravel's expected environment variables. Assuming your configuration isn't too heavily customized, your application should be able to automatically reads the correct values!

In our example above, I didn't add any services, and I removed the step that uses Node to build our static assets - we didn't it in our case!

chipper ci successful build

That was pretty quick! We didn't need to muck about with creating a custom Docker image, or figuring out a thousand different configurations. It just worked!

Fancier Yaml

If you need or want to get fancier, you can bring in more services, and decide when to build your application. Here's an example chipperci.yml file:

version: 1

environment:
  php: 8.1
  node: 14

services:
  - redis:
  - mysql: 5.7

on:
  pull_request:
    branches: .*
  push:
    branches:
      - develop
      - main

pipeline:
  - name: Setup
    cmd: |
      cp -v .env.example .env
      composer install -q --no-interaction --prefer-dist --optimize-autoloader
      php artisan key:generate
      php artisan migrate:seed

  - name: Compile Dev Assets
    cmd: |
      npm ci --no-audit
      npm run dev

  - name: ESLint
    cmd: npm run lint

  - name: Run Tests
    cmd: phpunit

This Yaml file does a few more things than our first example:

  1. We bring in Redis and MySQL services
  2. We use Build Triggers to build on any pull request, or any pushes to the develop or main branches (ignoring all other branches).
  3. Our pipeline scripts that setup and run the application run a migration (we didn't need extra configuration to connect to the MySQL database!), and then compile static assets (needed by our tests)

Deploying

How you deploy depends on where your site is hosted. Many people use Forge, Envoyer, Vapor, or Fly to deploy their Laravel application.

These deployments boil down to either sending a webhook, or running a command. We can see how we might do that in our .chipperci.yml file here. There's no magic!

I'll show a few examples of what it might look like to conditionally deploy to a production vs staging environment.

pipeline:
  - name: Setup
    cmd: |
      cp -v .env.example .env
      composer install -q --no-interaction --prefer-dist --optimize-autoloader
      php artisan key:generate
      php artisan migrate:seed

  - name: Compile Dev Assets
    cmd: |
      npm ci --no-audit
      npm run dev

  - name: ESLint
    cmd: npm run lint

  - name: Run Tests
    cmd: phpunit

  - name: Deploy To Many Services
    cmd: |
      if [[ $CI_COMMIT_BRANCH == 'master' ]]; then
          curl -X POST https://my-webhook-url/master/some-token \
              -d branch=$CI_COMMIT_BRANCH \
              -d commit=$CI_COMMIT_SHA

          vapor deploy production

          fly deploy --remote-only -a my-new-app-production
      fi

      if [[ $CI_COMMIT_BRANCH == 'develop' ]]; then
          curl -X POST https://my-webhook-url/develop/some-token \
              -d branch=$CI_COMMIT_BRANCH \
              -d commit=$CI_COMMIT_SHA

          vapor deploy staging

          fly deploy --remote-only -a my-new-app-staging
      fi

We have examples of sending a webook with curl, running the vapor deploy command, or running fly deploy. These run conditionally based on the branch is pushed to, using the provided environment variables.

CI/CD for Laravel

Chipper CI tries to be the easiest solution for CI/CD with Laravel. The environments are pre-configured for you - all you need to do is select a few options.

It's simple and powerful, enabling you to do what's most important - write code, and get out of your way so you can test / deploy your applications without headaches.

Try out Chipper CI!
Chipper CI is the easiest way to test and deploy your Laravel applications. Try it out - it's free!