GitLab CE, Docker, PHP7.1, Laravel 5.5, SQLite CI pipeline
Posted on 02 December 2017
5 minute read
I've been coding in PHP for more than 15 years using a variety of environments; Windows, FreeBSD, Linux, MacOS, VMWare+Vagrant+Linux, but more recently, I've been wanting to make the move to Docker.
I've also gone through various methods of working with code bases, from duplicating directories and incrementing version numbers, SVN and now Git, but unlike many, I don't much use GitHub for personal projects and prefer my self-hosted instance of GitLab CE. Git and GitLab have worked fine for some time, but I've recently started thinking about CI. This posed a few issues, but I managed to get it running with GitLab CI using a shell executor
. This was OK, it's my own code running on a server I run and maintain myself in my home office, but what if I want to run things under different circumstances, PHP version for example? Welcome Docker =)
I started with a PHP image from phpdockerio, shelled into the container, added some modules etc, logged out of the container and selected to use my local image. Many of you already fluent in Docker will quickly realise this doesn't work! You need more than that... being the Docker n00b that I am, I started to search the interwebz as to why my updates hadn't survived... doh! I need to commit my changes.
Docker environment
So, let's start at the beginning -- I will however, assume that you have Docker installed and running.
Let's pull the initial image
# docker pull phpdockerio/php71-fpm
Once the image has been installed, we can shell into it
# docker run -it phpdockerio/php71-fpm bash
We're now in our container. Let's make sure the OS is updated
# apt update
# apt upgrade -y
Great... now we can install a few extras for PHP
# apt install php-pear php-dev php-xdebug php-mbstring
This will install PEAR, PECL, Xdebug and the PHP MBstring module (I needed this for Laravel projects at the very least).
Let's also make sure we have the latest version of composer installed
# cd /usr/local/bin
# rm composer
# curl -o composer.phar https://getcomposer.org/composer.phar
# chmod 0755 composer.phar
# ln -s composer.phar composer
We should now be good to go. Laravel includes PHPUnit in composer.json
, we now have a core install of composer
and we have included Xdebug
for code coverage reporting.
Before we logout of the container, we need to commit the changes. Open a new terminal and check the running docker containers
# docker ps
40f99b35c775 phpdockerio/php71-fpm "bash"
# docker commit 40f99b35c775 mycontainers/php71-fpm:1.0.0
Let's check to make sure the new image has been created
# docker images
REPOSITORY TAG IMAGE ID
mycontainers/php71-fpm 1.0.0 2feaea24d6a3
Now we have our new image with all the extras included, we can remove the original
# docker rmi -f phpdockerio/php71-fpm:latest
Next up, we need to configure our GitLab runner. You can find the info for this over on GitLab's Docker Runner documentation page.
Now we need to update the runner config to use our newly created image
# docker exec -it gitlab-runner bash
Once inside the container, edit the runner config located at /etc/gitlab-runner/config.toml
and add a pull_policy
entry of never
[[runners]]
name = "docker-container"
url = "https://gitlab.mydomain.com/ci"
token = "{your-token-here}"
executor = "docker"
[runners.docker]
tls_verify = false
image = "mycontainers/php71-fpm:1.0.0"
privileged = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
pull_policy = "never"
[runners.cache]
This will make sure that your pipeline only uses your local docker image. Save the change and logout of the container.
GitLab CI config
If you haven't already, create a gitlab-ci.yml
file in the root of your project. For my most recent project project, I'm using an SQLite database.
First, we define an image to use -- this will be our newly created PHP docker image
image: mycontainers/php71-fpm
We can also cache items between builds, so let's cache our vendor
directory
cache:
paths:
- src/vendor/
I have my code within a src
directory, so before we do anything, I change to that dir and also make sure that composer is the most current
before_script:
- cd src
- composer self-update
As this project is using SQLite, I have no services
or variable
requirements, so the next stage is the test
where we run through PHPUnit.
I use a separate .env.testing
env file so I can commit this safely to the repo and modify it just for tests, so we copy it as .env
, update the path to the SQLite database, generate a new app key, create the cache, create the database and run the test suite
test:
script:
- cp .env.testing .env
- sed -i "s@DB_DATABASE=@DB_DATABASE=$(pwd)/database/appdatabase\.db@" .env
- php artisan key:generate
- php artisan config:cache
- touch ./database/appdatabase.db
- php artisan migrate:refresh --seed
- vendor/bin/phpunit --configuration phpunit.xml --colors=never --coverage-text
The complete gitlab-ci.yml
file should then look like
# Select local image
image: mycontainers/php71-fpm:1.0.0
# Select what we should cache between builds
cache:
paths:
- src/vendor/
before_script:
- cd src
- composer self-update
test:
script:
- cp .env.testing .env
- sed -i "s@DB_DATABASE=@DB_DATABASE=$(pwd)/database/appdatabase\.db@" .env
- php artisan key:generate
- php artisan config:cache
- touch ./database/appdatabase.db
- php artisan migrate:refresh --seed
- vendor/bin/phpunit --configuration phpunit.xml --colors=never --coverage-text
If everything has gone well (including your tests and code), we should have a successful process with the all important icon =)
This is about the simplest pipeline I could create with this scenario. YMMV in what you need / want in regards to modules and more complex applications (mine is a simple app in the early stages of development) but I hope this may help someone else thinking about going down this path.