CI with Laravel/MySQL/Cypress using Github Actions
Tags: github, beginners, testing, showdev
Published: 2020-08-16 02:06:18

For one of my projects, I decided to combine learning Cypress and putting together a CI pipeline, adding in some realism by finding an existing project online, the fantastic <a href="https://github.com/lupanvi/laragram">Laragram</a> by Luis Panta. 

## End result

``` 
name: CI
on:
  pull_request:
    branches: [master, development]
jobs:
  build-and-run-tests:
    runs-on: ubuntu-16.04

    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: forge
          MYSQL_DATABASE: forge
          DB_HOST: 127.0.0.1
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping" 
          --health-interval=10s 
          --health-timeout=5s 
          --health-retries=3
    steps:
    - name: Checkout
      uses: actions/checkout@v1

    - name: Lint
      uses: overtrue/phplint@master

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

    - name: Use Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '12.x'

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

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

    - name: Build site
      run: npm run dev

    - name: Execute unit tests via PHPUnit
      run: vendor/bin/phpunit
      env: 
        MYSQL_DATABASE: forge
        DB_USERNAME: root
        DB_PASSWORD: forge
        DB_PORT: ${{ job.services.mysql.ports[3306] }}

    - name: Migrate MySQL DB
      env: 
        DB_CONNECTION: mysql
        DB_DATABASE: forge
        DB_PORT: 3306
        DB_USER: root
        DB_PASSWORD: forge
      run: php artisan migrate:fresh --seed

    - name: Cypress run
      uses: cypress-io/github-action@v1
      with:
        browser: chrome
        install: false
        start: npm start
        wait-on: http://localhost:8000
        config-file: cypress.json
        spec: cypress/integration/laragram/laragram.spec.js

    - uses: actions/upload-artifact@master
      # there might be no screenshots created when:
      # - there are no test failures
      # so only upload screenshots if previous step has failed
      if: failure()
      with:
        name: screenshots
        path: cypress/screenshots
    # video should always be generated
    - uses: actions/upload-artifact@master
      with:
        name: videos
        path: cypress/videos
```

## Journey

As part of this, I decided that most of the env characteristics would be immutable. I could not change them. However, I did create a .env.ci file. I also thought that the test environment should echo the production environment as much as possible.
 
CI/CD is new to me, so I decided I would start small.  I would use the agile principle below, and write with verbosity.
1. Make it work, then try and break it
2. Make it right
3. Make it fast

I had a couple of ideas in mind for the tests I would add to it
- check the code style is consistent 
- run all the unit tests
- create UI tests for the core user journeys
so I would run a linter, unit tests, and Cypress. 

My first goal was to create a custom Docker image by imitating the setup on my local environment. So I looked at the Readme's setup instructions and the .env files to get an idea of where to start.

Then once I got it working, I would try some things to see if I added anything unnecessary, then add Continuous Deployment. Right now, I'm at 'got it working', time to delete any unnecessary cruft. This was a fascinating process.

## Extra learning exercise

I built my own version of cypress-io/github-action@v1, and ran it on my local environment, because I couldn't work out why my build wasn't working and I wanted to rule it out. It turned out I migrated my database incorrectly, but here's the code that imitates cypress-io/github-action@v1 anyway for our education cases.

```
"npm run server:test": "start-server-and-test start 8000 test:ci"
```
which calls
```
"test:ci": "npx cypress run --spec cypress/integration/laragram/laragram.spec.js --env CYPRESS_BASE_URL=http://l27.0.0.1:8000 --browser chrome"
```
this does the same thing as 
```
     - name: Cypress run
      uses: cypress-io/github-action@v1
      with:
        browser: chrome
        install: false
        start: npm start
        wait-on: http://localhost:8000
        config-file: cypress.json
        spec: cypress/integration/laragram/laragram.spec.js  
``` 
## Starting and using a MySQL DB with Laravel on Github Actions
 
``` 
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: forge
          MYSQL_DATABASE: forge
          DB_HOST: 127.0.0.1
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping" 
          --health-interval=10s 
          --health-timeout=5s 
          --health-retries=3

    - name: Migrate MySQL DB
      env: 
        DB_CONNECTION: mysql
        DB_DATABASE: {DATABASE}
        DB_PORT: 3306
        DB_USER: {USER}
        DB_PASSWORD: {PASSWORD}
run: php artisan migrate:fresh --seed
``` 



PS: A green build doesn't always indicate success. 

![Tests didn't start as Node needed to be downloaded first. Still green](https://dev-to-uploads.s3.amazonaws.com/i/byaujxp35kaxzvqbkb8g.png)
<figcaption>Tests didn't start as Cypress wasn't downloaded properly. Still green</figcaption>