Using Github actions and Deployer for creating CI/CD for Laravel
Tags: article, laravel, tutorial, cd
Published: 2020-01-29 12:30:21

---
title: Using Github actions and Deployer for creating CI/CD for Laravel
published: true
date: 2020-01-29 12:04:07 UTC
tags: Article,Laravel,Tutorial,cd
canonical_url: https://www.danielwerner.dev/using-github-actions-and-deployer-for-creating-ci-cd-for-laravel
---

In one of my previous articles I’ve written about how to create an automated pipeline for [continuous integration and continuous delivery using travis](https://www.danielwerner.dev/set-up-ci-cd-for-your-laravel-app-with-github-travis-and-deployer/). Lately [GitHub actions](https://github.com/features/actions) are quite popular, I am also experimenting with them. I started by reading the articles written by the guys from Spatie, you can find them [here](https://freek.dev/1546-using-github-actions-to-run-the-tests-of-laravel-projects-and-packages) and [here](https://rubenvanassche.com/getting-started-with-github-actions/). While setting up the actions I encountered some issues with the mysql service, I’ll dive into it later. Let’s set up the same pipeline as last time. Run the tests for every push and deploy the application if the tests are successful.

## TL;DR;

For those who don’t have time to read the whole article, here is the final version of the yaml configuration:

```
name: Build

on: [push, pull_request]

jobs:
  laravel-tests:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: tracy_test
        ports:
          - 3307:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    steps:
    - uses: actions/checkout@v1
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.ci', '.env');"
    - name: Install Dependencies
      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: Set up Passport
      run: php artisan passport:keys
    - name: Execute tests (Unit and Feature tests) via PHPUnit
      run: vendor/bin/phpunit

  deploy:
    if: github.ref == 'refs/heads/master'
    needs: [laravel-tests]
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Deploy
      env:
        DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
      run: |
        eval "$(ssh-agent -s)"
        ssh-add - <<< "${DEPLOY_KEY}"
        mkdir ~/.ssh
        echo -e "HostName example.com\n\tStrictHostKeyChecking no\n\t"User deploy >> ~/.ssh/config
        composer install
        vendor/bin/dep deploy     
```

## Create the workflow

First we need to create a workflow by creating a .yaml file in the **.github/workflows** directory, for example: **.github/workflows/laravel.yml**. In this file we’re going to define the runner environment, the jobs and the steps to run.

We are defining the workflow to run on push and pull request events:

```
on: [push, pull_request]
```

We’ll have 2 jobs to run **laravel-tests** and **deploy**.

## Run the tests

Let’s take a look what steps are necessary for running the tests.

Checkout the code

```
- uses: actions/checkout@v1
```

Create the .env file. For this step we assume you have and .env.ci file in your project (it should be committed to the repository!)

```
- name: Copy .env 
  run: php -r "file_exists('.env') || copy('.env.ci', '.env');"
```

Install dependencies with composer

```
- name: Install Dependencies
  run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
```

Generate the application key

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

Create passport keys. This step is optional, only necessary if your project is using passport

```
- name: Set up Passport
  run: php artisan passport:keys
```

And the last step, run the tests

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

For testing you can use sqlite database which is very quick but does not have all the features as other databases such as MySQL. For my use case I am using the mysql service because the project uses spatial data types.

When I first time set up the mysql service, according to the articles I’ve read and according to the documentation, I got the following error when running the tests: **SQLSTATE[HY000] [1049] Unknown database**. After some experimenting, I realized that the GitHub runners come with preinstalled software (you can find the list [here](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/software-installed-on-github-hosted-runners#ubuntu-1804-lts)). They also have mysql preinstalled, but when you define the mysql service, it starts a mysql docker container.

I came up with two solutions:

- Create the test database in the preinstalled mysql server:

```
- name: Create database
  run: mysql -uroot -proot -e 'create database tracy_test;'
```

- Use a different port for the docker service, and define the database in the service definition

```
services:
  mysql:
    image: mysql:5.7
    env:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: tracy_test
    ports:
      - 3307:3306
    options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
```

Please note the ports section it cannot run on the same port as the built in mysql service, so it is using 3307 for the mysql. In this case you’d need to set up the .env.ci accordingly:

```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3307
DB_DATABASE=tracy_test
DB_USERNAME=root
DB_PASSWORD=root 
```

If you have other solution(s) for the mysql service problem, please let me know in comments.

## Deploy

I am big fan of deployer, although there are plenty of other solutions for deployment like AWS, Docker etc. For my personal/small scale projects deployer just works well. I won’t go into the details of the server side setup, I did it in a previous article, if you are interested please check it [here](https://www.danielwerner.dev/set-up-ci-cd-for-your-laravel-app-with-github-travis-and-deployer/).

We need to allow the login from GitHub actions to our server. Generate a private/public key pair, add the public key to the authorized_keys of your deploy user on the server. Add the private key as a secret to your repository, you can find it under **Settings/Secrets** :

![](https://www.danielwerner.dev/wp-content/uploads/2020/01/Screen-Shot-2020-01-29-at-10.52.24-AM-1024x432.png)

We only want to deploy form the master branch, and only if all the test have passed.

```
deploy:
  if: github.ref == 'refs/heads/master'
  needs: [laravel-tests]
```

The **needs** key ensures that the deploy only runs when the tests passed.

Add the DEPLOY_KEY to the env variables

```
- name: Deploy
  env:
    DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
```

Set up the ssh agent to use your key for login

```
run: |
  eval "$(ssh-agent -s)"
  ssh-add - <<< "${DEPLOY_KEY}"
  mkdir ~/.ssh
  echo -e "HostName example.com\n\tStrictHostKeyChecking no\n\t"User deploy >> ~/.ssh/config
```

Run composer install and deploy the project:

```
composer install
vendor/bin/dep deploy
```

If everything goes well the tests run and the project gets deployed automatically after each push on the master branch.

So far the GitHub actions seem to work well, probably I’ll use it for CI/CD related tasks for all my projects in the future.

If you have any suggestions or problem setting this up, please feel free to share them in comments.

The post [Using Github actions and Deployer for creating CI/CD for Laravel](https://www.danielwerner.dev/using-github-actions-and-deployer-for-creating-ci-cd-for-laravel/) appeared first on [Daniel Werner](https://www.danielwerner.dev).