Continuous Integration with GitHub Actions and Laravel 6

GitHub Actions (https://github.com/features/actions) is a powerful tool provided by GitHub for continuous integration.

I just published an updated article for Laravel 7 + MySQL + managing secrets with GitHub Actions: https://medium.com/swlh/test-automation-for-laravel-7-and-mysql-with-github-actions-467493c2f7ed

GitHub Actions allows you to execute some commands when a GitHub event is triggered. For example you can automate the execution of unit test of your code when you push your code in the repository.

GitHub Actions provide you an environment where you can setup the technology stack, fetch your code from the repository, configure your software and execute some command.

The scenario that I would like to explain you is:

I have a Web App based on Laravel 6, every time I push some new code on master branch on the GitHub repository, I would like to execute automatically Unit tests and Feature tests.

The requirements are:

  • you have a Laravel application;
  • you have a specific .env file for continuous integration (for example .env.ci)
  • you have a GitHub repository where your can push your code;
  • you have some tests to execute (by default, a Laravel application has 2 basic tests).

With GithHub Actions we are going to totally automate these step:

  • Setup a virtual environment with the operating system installed;
  • Clone the branch from your repository;
  • copy the .env.ci file in .env to be sure to have the right configuration for continuous integration;
  • install all Laravel Framework dependencies;
  • execute all migration (create database and all tables of database);
  • execute tests via PHPUnit.

Setup your first workflow with GitHub Action

If you log you into GitHub and select your repository, you will se a tab named “Actions”

Image for post
Image for post
Actions tab in your GitHub repository

Click on Actions tab to access to setup your new workflow.

In “Actions” page, you can choose to start from a template or setup a new workflow from scratch. In this case I suggest you to select and click “Set up a workflow yourself”.

Set up a workflow from scratch

Automatically will be created a new file main.yml in a new directory .github/workflows .

This file contains some basic directive in order to have a minimal Yaml configuration file.

Image for post
Image for post
the new YAML file auto-generated

The file controls the behaviour of the action and contains some informations like:

  • name: the name of the workflow;
  • on: the event that trigger the execution of the action. It could be also an array (so the action could be triggered by multiple events);
  • jobs: the list of jobs to be executed. Jobs by default are executed in parallel. You can define a kind of dependency trees with the needs directive if you want to have some execution sequence instead of parallel execution;
  • runs-on: is the name of the image of the virtual machine to be used. If you specify ubuntu-latest you should have a PHP stack installed;
  • steps: each job contains a sequence of tasks called steps. Steps can run commands. We will see some example of steps section later with an example.

Configure the YML workflow file for Laravel

The file created is main.yml. You can change the name of the file if you prefer. The important thing is that it needs to be stored into .github/workflows directory.

Image for post
Image for post
Yaml workflow file

Now, we are going to walk through the configuration.

The gists with the YML configuration file

Define events

You can define the GitHub events that trigger automatically the workflow. For example, if you want to trigger and execute the workflow when developers push the code on master (or features/) branch you can use the directive “on”.

name: Continuous Integration Laravel 6
on:
push:
branches:
- master
- features/*

We can define the name of the Workflow with the directive “name” and we can define the event “push” in the “on” section . In the “push” section, you can list (optionally) the branches. If you don’t specify branches section the workflow will be triggered on every push and not only on the push on the specific listed branches.

The directive “name” is also important because it will be used by GitHub in the workflow list, to identify the workflow (you could create more than one workflows).

Image for post
Image for post
The Workflow in the GitHub Actions UI

The jobs

With a workflow you can define one or more jobs. A job is a specific task that needs to be executed in the workflow. You can configure to run jobs in parallel or with some dependencies (for example: run the job “Test” only when the job “build assets” is completed)

A job can be executed on a specific operating system. For Laravel you could use a last version of Ubuntu system.

jobs:
laravel-tests:
runs-on: ubuntu-latest

In my Yaml file I used “laravel-tests” as job name and “ubuntu-latest” as operating system.

The steps

Each jobs has multiple steps. In each step you can “run” your commands or you can “use” some standard actions.

Following the Yaml syntax, each step starts with “minus sign”. Step that uses standard action is identified with “uses” directive, steps that use custom commands are identified by “name” and “run” directive. “name” is used in the execution log as a label in the GitHub Actions UI, “run” is used for launch the command. With “run” directive you could define command with arguments and parameters. With “run” you can also list a set of commands.

name: Continuous Integration Laravel 6
on:
push:
branches:
- master
- features/*
jobs:
laravel-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Copy ENV Laravel Configuration for CI
run: php -r "file_exists('.env') || copy('.env.ci', '.env');"
- name: Install Dependencies (PHP vendors)
run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
- name: Generate key
run: php artisan key:generate
- name: Create DB and schemas
run: |
mkdir -p database
touch database/database.sqlite
php artisan migrate
- name: Execute tests (Unit and Feature tests) via PHPUnit
run: vendor/bin/phpunit

Let me walk-through all steps in the job “laravel-tests”.

First step: git checkout

The first step is retrieve the sources. To do that, GitHub Actions has a standard action identified with “actions/checkout@v1”. To perform the action you need to set a step:

- uses: actions/checkout@v1

Second Step: copy the right .env file

In Laravel you can use .env to store your keys and parameters for the environment configuration. For example: database connection parameters, cache connection parameters, smtp configuration etc.

In order to create a database and the schema (tables) needed by your application, I suggest to create a .env.ci (for the continuous integration) file that use a sqlite database. I suggest to commit/push the .env.ci file on the repository. The workflow will copy .env.ci into .env file so your continuous integration environment will use a specific configuration.

I suggest you to set your database connection in your .env.ci file:

DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite
# DB_CONNECTION=mysql
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

In order to create a right .env.ci file I suggest you to you to copy .env.example into .env.ci and the comment out the line about DB_ and add DB_CONNECTION and DB_DATABASE that uses sqlite database (as you can see in the example above).

Once you have your .env.ci file you can define in the Yaml file:

    - name: Copy ENV Laravel Configuration for CI
run: php -r "file_exists('.env') || copy('.env.ci', '.env');"

Third step: install dependencies

Once you have your code checked out and you have your .env file, you need to install all dependencies in your composer.json:

    - name: Install Dependencies (PHP vendors)
run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist

Fourth step: setup your Laravel key

    - name: Generate key
run: php artisan key:generate

Fifth step: create your database

Every time you launch the workflow you start your environment from scratch so you need to create (every time) your database and then execute a migrate command.

    - name: Create DB and schemas
run: |
mkdir -p database
touch database/database.sqlite
php artisan migrate

In a step you can define more command. To use more command in one step use pipe | character in run line.

Last step: execute the tests

Finally you can execute your test via php unit. I suggest you to use phpunit command in your vendor files:

    - name: Execute tests (Unit and Feature tests) via PHPUnit
run: vendor/bin/phpunit

Is that all?

This is a small configuration that allow you to execute your firsts test in automate way.

Image for post
Image for post
The GitHub Actions UI with the execution of the workflow.

For sure you could extend this configuration and maybe you could add some other job like building frontend assets etc.

Please write me your feedback or suggestion in the comment in order to improve this article.

UPDATE: i just created a video tutorial about Github Actions and Laravel:

Let’s automate everything!

Written by

I’m technophile. Vuejs and Laravel enthusiast! #vuejs #laravel. I love #coding

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store