A working gitlab Laravel pipeline with PHPUnit, code style, and more
By: Simon Auer
Tags: Deployment, Gitlab, Ci Cd Pipeline, Phpunit, Laravel

How we deploy our PHP/laravel applications using environments and quality assurance checks.

Intro

I am a big fan of process optimization and I love trying out new ways to develop, deploy, and work on big software projects.

As head of a software development agency in Vienna, Austria, I was lucky enough to have worked on over 100 big web and mobile applications over the last 15 years with startups, international companies, and other amazing developers to try, fail and refine these processes over and over again.

The following approach is our current approach to deploying laravel projects.

To get this out of the way: If you have feedback, suggestions on that, please share them with us in the comments! I would love to hear your thoughts and the approach that works well for you.

We use gitlab for continuous integration/deployment, but this should work similarly also with other tools like Jenkins, ….

More information about the environment approach

I already wrote a detailed blog post about the reasoning and workflow for development, staging, and production environment and how we deal with it.

You can find it here:

How to set up your staging environment for web applications

If you are confused or need more information on how this gitlab-ci file came to be or detailed instruction on how to set your server and gitlab up to work with it, you can find it here:

Deploy PHP/Laravel applications using Gitlab CI/CD

Our gitlab-ci file

To make sure our code works and looks nice, we also run a few quality assurance tasks on it, before we deploy.

Here is our full gitlab-ci.yml file

image: lorisleiva/laravel-docker:7.4 # the number here resembles the php version, so you might have to adjust it to 8.0, ... ## templates .init_ssh: &init_ssh |

mkdir -p ~/.ssh

chmod 700 ~/.ssh

[[ -f /.dockerenv ]] && echo -e "Host *

\tStrictHostKeyChecking no



" > ~/.ssh/config .change_file_permissions: &change_file_permissions |

find . -type f -not -path "./vendor/*" -exec chmod 664 {} \;

find . -type d -not -path "./vendor/*" -exec chmod 775 {} \; ### the stages we go through stages:

- build

- test

- deploy ## prepare composer:

stage: build

cache:

key: ${CI_COMMIT_REF_SLUG}-composer

paths:

- vendor/

script:

- *init_ssh

- composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts

- cp .env.gitlab .env

- mv .env.testing .env.testing--not-used

- cp .env.testing.gitlab .env.testing

- touch database/database.sqlite

- php artisan app:install --refresh --seed --demo artifacts:

expire_in: 1 month

paths:

- vendor/

- .env

- .env.testing

- database/database.sqlite

only:

- master

- staging

- production

- development ## QA codestyle:

stage: test

dependencies: []

script:

- phpcs --standard=PSR2 --extensions=php --ignore=app/Support/helpers.php app

only:

- master

- development

- production phpunit:

stage: test

dependencies:

- composer

script:

- apk add --update-cache xvfb dbus ttf-freefont fontconfig wkhtmltopdf

- phpunit

artifacts:

expire_in: 1 month

paths:

- .env

- .env.testing

- report

- database/database.sqlite

only:

- master

- development

- production ## DEPLOYMENT

stage: deploy

script:

- eval $(ssh-agent -s) && echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null

- *init_ssh

- *change_file_permissions

- php artisan deploy {MY_DOMAIN}.devserver.at -s upload

environment:

name: development

url:

only:

- development deploy_development:stage: deployscript:- eval $(ssh-agent -s) && echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null- *init_ssh- *change_file_permissions- php artisan deploy {MY_DOMAIN}.devserver.at -s uploadenvironment:name: developmenturl: https://{MY_DOMAIN}. devserver.atonly:- development

stage: deploy

script:

- eval $(ssh-agent -s) && echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null

- *init_ssh

- *change_file_permissions

- php artisan deploy {MY_DOMAIN}.stagingserver.at -s upload

environment:

name: staging

url:

only:

- master deploy_staging:stage: deployscript:- eval $(ssh-agent -s) && echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null- *init_ssh- *change_file_permissions- php artisan deploy {MY_DOMAIN}.stagingserver.at -s uploadenvironment:name: stagingurl: https://{MY_DOMAIN}. stagingserver.atonly:- master

stage: deploy

script:

- eval $(ssh-agent -s) && echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null

- *init_ssh

- *change_file_permissions

- php artisan deploy {MY_DOMAIN} -s upload

environment:

name: production

url:

only:

- production deploy_production:stage: deployscript:- eval $(ssh-agent -s) && echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null- *init_ssh- *change_file_permissions- php artisan deploy {MY_DOMAIN} -s uploadenvironment:name: productionurl: https://{MY_DOMAIN} only:- production

Here we have added the following things:

in composer step:

We defined alternative .env files for gitlab and for running our tests.

They contain different environment settings, like the database connection (they use sqllite as the main database inside the gitlab pipeline and also change the APP URL to use `http://localhost` if we have e2e tests)

- cp .env.gitlab .env

- mv .env.testing .env.testing--not-used

- cp .env.testing.gitlab .env.testing

- touch database/database.sqlite

- php artisan app:install --refresh --seed --demo

checking code style:

Here we check to make sure that all of our developers used the PSR2 coding style.

codestyle:

stage: test

dependencies: []

script:

- phpcs --standard=PSR2 --extensions=php

only:

- master

- development

- production

running PHPUnit tests:

Using this we run the PHP unit tests and also save the report and SQLite database file as an artifact, so we can download and debug it if something went wrong.

phpunit:

stage: test

dependencies:

- composer

script:

- phpunit

artifacts:

expire_in: 1 month

paths:

- report

- database/database.sqlite

only:

- master

- development

- production

Thank you very much for taking the time to read this.

If you went this far down, I would really appreciate your comment and input.

How are you guys doing it?

I will post more about DevOps, deployment, coding (especially react, flutter and laravel) in the future, so follow me to read more ;)