DEV Community

Daniel Werner
Daniel Werner

Posted on • Originally published at danielwerner.dev on

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. Lately GitHub 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 and here. 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     
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');"
Enter fullscreen mode Exit fullscreen mode

Install dependencies with composer

- name: Install Dependencies
  run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
Enter fullscreen mode Exit fullscreen mode

Generate the application key

- name: Generate key
  run: php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

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

- name: Set up Passport
  run: php artisan passport:keys
Enter fullscreen mode Exit fullscreen mode

And the last step, run the tests

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

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). 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;'
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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.

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 :

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]
Enter fullscreen mode Exit fullscreen mode

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 }}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Run composer install and deploy the project:

composer install
vendor/bin/dep deploy
Enter fullscreen mode Exit fullscreen mode

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 appeared first on Daniel Werner.

Top comments (0)