How to do continuous integration (CI) with a Laravel Nova project that also does database testing

Automated testing and continuous integration are huge topics. I find even experienced programmers sometimes completely miss the point and skip both. To me, this is a selfish act and extremely presumptuous - do you really think you're client deserves that you will be forever the only programmer on their project?

Blog Featured Image Placeholder

I use Laravel Nova in many of my projects. I also use GitHub Actions and for my private clients of course I have private repos. So far I've battled to get Laravel Nova working nicely in GitHub Action's in continuous integration environment. Totally, finally, I got this right.

The components that has to play together are CI and GitHub Actions and authentication and MySQL.

Part of the problem is that the Laravel Nova has extremely scant information on how to do actual authentication and if authentication to nova.laravel.com fails in the pipeline your tests will never run.

The current documentation refers to "CodeShip" and I clearly remember how they've hyperlinked to other CI tools in the past. Their comment about storing auth.json in source control is apt but also misguided because they don't give clear instructions on how to use it with something as common as GitHub Actions.

Moving on swiftly I only got this working by creating an auth.json file in the root of my project. The command given in the Laravel Nova documentation, namely this one:

composer config http-basic.nova.laravel.com ${NOVA_USERNAME} ${NOVA_LICENSE_KEY}

does not help. Instead, this is what I had to do:

cat auth.json 
{
    "http-basic": {
        "nova.laravel.com": {
            "username": "user@example.com",
            "password": "nova-license-key"
        }
    }
}

Once you have the licensing problem sorted out you can move over to what the YAML file must look like. I used Spatie's Package Skeleton for Laravel as a base and then found MySQL specific information in this file made by Shivam Mathur.

The end result of my working run-tests.yml is this:

name: tests

on:
  push:
  pull_request:

jobs:
  tests:
    runs-on: ubuntu-22.04

    strategy:
      fail-fast: true
      matrix:
        php: ['8.1']
        laravel: [9]

    name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}
    env:
      DB_DATABASE: laravel
      DB_USERNAME: root
      DB_PASSWORD: password
      NOVA_USERNAME: ${{ secrets.NOVA_USERNAME }}
      NOVA_LICENSE_KEY: ${{ secrets.NOVA_LICENSE_KEY }}

    # See https://github.com/shivammathur/setup-php/blob/master/examples/laravel-mysql.yml
    # Docs: https://docs.github.com/en/actions/using-containerized-services
    # Had to include DB_PORT for last test
    services:
      mysql:
        image: mysql:latest
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: false
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: laravel
        ports:
          - 3306/tcp
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          extensions: dom, curl, libxml, mbstring, zip, bcmath
          ini-values: error_reporting=E_ALL
          tools: composer:v2
          coverage: none

      - name: Install dependencies
        run: |
          composer require "illuminate/contracts=^${{ matrix.laravel }}" --no-update
          composer update --prefer-dist --no-interaction --no-progress

      - name: Prepare Laravel Application
        run: |
          cp .env.ci .env
          php artisan key:generate

      - name: Execute tests
        run: vendor/bin/phpunit --verbose
        env:
          DB_PORT: ${{ job.services.mysql.ports['3306'] }}

The caveats and extras I had to put in apart from what's in the Spatie boilerplate is this:

  • A services section to load a MySQL container. It's still abundantly unclear to me if I really need this, but anyhow, this one works. It certainly slows down the test but at least it's working.

  • A "Prepare Lavel Application" section to copy a .env.ci file over .env

  • Key generation

  • DB_PORT in the "Execute tests" section.

You'll also notice a NOVA_USERNAME and NOVA_LICENSE_KEY sections. This was a feeble attempt to implement variables based secrets in GitHub actions but it has no effect and the auth.json is still required.

Conclusion

CI is a power tool when used with automated testing and Laravel. Getting it working is a slog and a lot of small caveats to get right but worth the effort.

Updated: 1 year ago

© 2022 Eugene's Blog