Automated pipeline tests, version bumping and deployments with Gitlab
This is one of my favorite topics! Setting up a fully automated deployment.
I want to share with you how to set up a fully automated deployment including tests and version bumping.
I am using PHP and Laravel as the sample project but please keep in mind that this post is not limited to Laravel and PHP only!
If you don’t want to read the post and jump to the Repo:
Story
When you push any commit to your main branch it will start testing, If it was successful then it will start to bump a new version and generate a tag for that commit. After that, you’ll see a tag on the tags page of Gitlab. To deploy that tag to your server, You just need to open the pipeline and start the deployment job!
If you feel confused, Don’t worry we’ll get back to this later in this post.
Setup
Create .gitlab-ci.yml
file at the root of your project and continue reading this post 🙂
Tests
Before we start, I assume that you have your project ready and your project has some tests. If it doesn’t have any tests (I hope it does 🙂), skip the test part.
To create test pipelines simply add the following content to your .gitlab-ci.yml
stages:
- test
test:
stage: test
image: php:8.1-fpm
services:
- mysql:lates
variables:
MYSQL_DATABASE: $DB_DATABASE
MYSQL_ROOT_PASSWORD: DbRootPass
DB_HOST: mysql
DB_USERNAME: root
DB_PASSWORD: DbRootPass
before_script:
- apt-get update
- apt-get install -qq libzip-dev git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev zip unzip
- docker-php-ext-install pdo_mysql zip
- curl --silent --show-error "https://getcomposer.org/installer" | php -- --install-dir=/usr/local/bin --filename=composer
script:
- cp .env.example .env
- composer install
- php artisan db:create
- php artisan key:generate
- php artisan --env=testing migrate
- php artisan test
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- vendor/
except:
refs:
- tags
variables:
- $CI_COMMIT_TITLE =~ /^RELEASE:.+$/
If you push these changes you’ll see a pipeline appears on Gitlab.
If you look carefully at the code above you will see that I’ve disabled running tests on the tags because the tests are being tested on the main branch so no need to test them again on tags! but it’s up to you.
Bumping Versions
What do I mean by this? So the idea is to generate a version using semantic versioning when there was a push to the main branch.
I am using Semantic Release library to achieve that. This library checks if there was a commit that can be considered a version then it will release a tag (version) for that. By default, it uses the Commit Analyzer with conversational commits, But you can change it of course. For example if you want to release a feature you must have a commit message like this: feat(something): something else
Extend the .gitlab-ci.yml
file with the following codes:
stages:
- test
- version
test:
...
version:
stage: version
image: node:alpine
before_script:
- apk add --no-cache git
- npm install @semantic-release/changelog @semantic-release/commit-analyzer @semantic-release/git @semantic-release/gitlab @semantic-release/npm @semantic-release/release-notes-generator conventional-changelog-conventionalcommits semantic-release
script:
- npx semantic-release
only:
- main
variables:
GL_TOKEN: $GL_TOKEN
except:
refs:
- tags
variables:
- $CI_COMMIT_TITLE =~ /^RELEASE:.+$/
Then create a Gitlab Token on your repository with write access and put it on the pipeline environments into GL_TOKEN
key.
And then put the following content into your package.json
file
...
"release": {
"branches": [
"main"
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator"
]
},
...
If you push these changes you will see some pipelines like below:
And if it was successful:
And you should be able to see the new version has been generated on the tags page.
Deployment
Let’s now deploy our project to the server 🚀
I assume that you have your production server ready and you already set it up and deployed the first version.
The plan is to create a new pipeline for the deployment and make it manual and only available on the tags! So you can deploy any tags (versions) manually by pressing a button.
First, SSH to your server and create a new user or use the current user for the deployments.
Generate an SSH key if you don’t have one already and make sure that you generate it without a passphrase.
# As the deployer user on server
# Copy the content of public key to authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
# Copy the private key text block
cat ~/.ssh/id_rsa
Copy the generated private key and store it on GitLab pipeline variables into PROD_SERVER_KEY
You should have these keys on your GitLab pipeline variables
PROD_SERVER_KEY -> The private key that you created
PROD_SERVER_IP -> Your server's IP Address
PROD_SERVER_PATH -> Root path of your application in the server
PROD_SERVER_USER -> Your server's user
Make sure all of them are unprotected and not masked so the pipeline can have access to them when running on the Tags.
Install Laravel Envoy and configure it according to my sample configuration
You can skip this step and set up your deployment script inside the .gitlab-ci.yml
Extend your .gitlab-ci.yml
and add deploy stage to it.
stages:
- test
- version
- deploy
test:
...
version:
...
deploy:
stage: deploy
image: php:8.1-fpm
variables:
PROD_SERVER_IP: $PROD_SERVER_IP
PROD_SERVER_USER: $PROD_SERVER_USER
PROD_SERVER_KEY: $PROD_SERVER_KEY
PROD_SERVER_PATH: $PROD_SERVER_PATH
before_script:
- apt-get update
- apt-get install -qq libzip-dev git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev zip unzip
- docker-php-ext-install pdo_mysql zip
- curl --silent --show-error "https://getcomposer.org/installer" | php -- --install-dir=/usr/local/bin --filename=composer
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client git -y )'
- eval $(ssh-agent -s)
- echo "$PROD_SERVER_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan "$PROD_SERVER_IP" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- composer install
- php ./vendor/bin/envoy run deploy
only:
- tags
when: manual
If you don’t use Laravel Envoy then just replace the script
section with your own script.
This configuration will generate a new pipeline that is available only in tags and it is manual like the screenshot below:
When you want to deploy any tag, Just open the pipeline and press the Play
button.
You can see the whole project’s source code here including the pipelines.
I hope you enjoyed ❤️