Beta version [Work In Progress]
This website is not optimized for mobiles, it's not fully responsive yet. Am still working to improve it step by step.
Few days ago, I was playing with the new Laravel 5.4 and Gitlab CI, and got to the point where I wanted the CI to test my code once I push it.
Using google lead me to this post on
Laracasts, and it was the starting point for me, there you can get important
.gitlab-ci.sh , basically the first one will define the stages for the testing and the
second one will build the environment for your test.
So lets look at a short version of
before_script: - bash .gitlab-ci.sh variables: MYSQL_DATABASE: project_name MYSQL_ROOT_PASSWORD: secret phpunit:php7.0:mysql5.6: image: php:7.0 services: - mysql:5.6 script: - php vendor/bin/phpunit --colors phpunit:php7.1:mysql5.6: image: php:7.1 services: - mysql:5.6 script: - php vendor/bin/phpunit --colors
Looking at the file we can figure out that the
before_script will run
.gitlab-ci.sh file before any tests and
variables define the variables for mysql and the other two is stages for executing the tests on two different php
images (you can check Laracasts post which have the full version of the file as they also test it on php 5.x).
But what is
services ? basically they are docker images which the CI is going to pull for you and link
them together so you can execute your tests, nice right :D, lastly
script is the command that you want to execute
inside your test image.
With those basics information, you have got a full testing CI environment for your Laravel application, which will be triggered whenever you pushed your code.
What about the auto deploy how we can do that? in Gitlab they have some integration with Docker and kubernetes which you can look at Gitlab documentations, but for me I use simple SSH methods which is nice and simple.
Lets talk about what we should do step by step:
Create Secret Variables:
GitLab CI allows you to define per-project secret variables that are set in the build environment. Those variables stored outside the repository and used within your CI so you dont need to add them as plain text. You can add your secret variables from within your repository Settings -> CI/CD Pipeline, once you are there you can define your secrets like the following images
Once you created them, you can use them in your
Optional, Create your own Docker image:
For me, I try to use any opportunity to learn the things I want to learn, and since the GitlabCI depends on Docker, so why don't I use my Docker knowledge to do something nice, plus am using Docker with PHPStorm so I wanted to use the same Docker image for both the CI and my development environment. The image am going to use is PHP7.1 with xDebug which I built and published on my Docker Hub.
Create your own .gitlab-ci.yml file:
Our gitlab-ci instruction file will contain mainly four parts:
- before_script: In this section we will define the commands which we want to use on each and every stage, before anything else within the stage scripts section.
- stages: In this section we will define the order which our system is going to work (Jobs from the same stage will run in parallel).
- Testing stage: In this section we tell the CI what we want to execute and which Docker image it should pull.
- Deploy Stage: In this section we just define the second job that we want the CI to execute.
So it will looks like the following:
before_script: - ' ! -e /.dockerenv && exit 0' stages: - phpunit - deploy variables: MYSQL_DATABASE: homestead MYSQL_ROOT_PASSWORD: secret phpunit:php7.1:mysql5.6: stage: phpunit only: - develop image: zaherg/php-7.1-xdebug-alpine:latest services: - mysql:5.6 script: - sh .gitlab-ci.sh deploy_develop: stage: deploy before_script: - mkdir -p ~/.ssh - echo -e "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ' -f /.dockerenv && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' script: - ssh sshuser@$DEV_SERVER "cd ~/www && git pull origin develop && ~/bin/composer install" environment: name: develop url: https://develop.zah.me/ only: - develop
Lets explain it section by section:
before_script: - ' ! -e /.dockerenv && exit 0'
First we need to make sure that we are running inside docker, if not then exit immediately.
stages: - phpunit - deploy variables: MYSQL_DATABASE: homestead MYSQL_ROOT_PASSWORD: secret
Second we defined the two stages we want to work with, and add a Docker variables which will be used with MySQL docker image.
phpunit:php7.1:mysql5.6: stage: phpunit only: - develop image: zaherg/php-7.1-xdebug-alpine:latest services: - mysql:5.6 script: - sh .gitlab-ci.sh
Third we define the testing stage for our Laravel code base, we first make sure to
say that this job belongs to the
phpunit stage, now since I need to test my code when I push it to develop branch
only I specified that on my
only section, then we specify
which PHP docker image we want to use, as I said before I use my own images, we specify the service which is linked to
our code which is mysql and finally we specify the commands we want to execute in the
I just grouped all the commands inside
.gitlab-ci.sh file which will do everything for me :
- configure composer and run it
- copy my testing env file to be the main one
- run migration and seeds
- and finally run phpunit
!/bin/sh set -eo pipefail Install php libraries. echo "Start the update and the install" composer config -g github-oauth.github.com $gitHubKey composer install --no-interaction --optimize-autoloader Copy over testing configuration. cp .env.testing .env Generate an application key. Re-cache. echo "Run artisan" php artisan key:generate php artisan optimize php artisan config:cache Run database migrations. echo "Run migration & Seeds" php artisan migrate --seed Run the tests. echo "Run the tests" php vendor/bin/phpunit --colors
As you can see I used the first secret variable out of the two we said we will define
Now whenever I push to develop branch, I got the CI to test my code and if it fail I'll get something like this
Which is really really nice thing.
Now we are going to be on our last stage, Auto deploy if the test went green.
deploy_develop: stage: deploy before_script: - mkdir -p ~/.ssh - echo -e "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ' -f /.dockerenv && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' script: - ssh sshuser@$DEV_SERVER "cd ~/www && git pull origin develop && ~/bin/composer install" environment: name: develop url: https://develop.remoteserver.com/ only: - develop
its really simple, if you read it you will notice that its not different than the previous stage except that we dont pull any Docker image, we only SSH into the remove server and pull the code from gitlab.
Make sure that the Private SSH key that you have added to your secret variables, can access your remote server.
First we define that this job belongs to the
deploy stage, then we overwrite the
before_script so it will execute
the one we define instead of the global on, basically this is what I did :
- Make sure that I create the
~/.sshfolder with in Gitlab Docker image, cause at the end everything run inside a container.
- I use the second secret key that we have defined which will contain my the private key to access the remote server.
- Make sure that the file is not fully open, SSH will complain if the file is not set as 600 mode.
- Check if we are running inside docker and then disabled StrictHostKeyChecking for all the hosts inside
Make sure that you either pulled your code once on the remote server to add gitlab servers to your know_hosts files, if the remote origin was not originally from gitlab.
environment section will define your environments variable
which you will see within your Environment section under the Pipelines section
You are free to create this manually or once you set them inside your
gitlab-ci.yml file they will be auto created
Now if you clicked on
develop you will see how many times you have sent your code to the server, and even you can
rollback to older version of your code and now whenever you pushed to develop your code will be tested and auto
deploy without any manual interaction from your side.
And that's all, let me know if you have anything else that you can add to enhance my post.
As always, you may find some lingual mistakes in my post, as english is not my mother language, so if you do and want to help me out, just let me know so I can fix it.