by ndf on 19-11-2018

GitLab CI for Drupal 8 websites

At Dx Experts we use GitLab as our primary system for task-planning and git-hosting.
GitLab is a competitor of GitHub and BitBucket.
One of its most amazing features is GitLab CI (GitLab Continues Integration). It is a task runner that can be compared with Jenkins and Rundeck.

In this article we teach you how to setup GitLab CI for Drupal 8 websites.
We use a simple approach that applies any committed changes to your Drupal 8 website automatically.

You can find example gitlab-ci templates for Drupal 8 and Drupal 7 here: https://gitlab.com/dx-experts/drupal-templates-for-gitlab-ci

Continues integration and automated deployments

Every Drupal 8 development team probably experienced difficulties related to deploying Drupal websites.

  • If you do it manually you must login to the hosting-server, then pull data from the correct git-branch, then run Composer command, then run a list of Drush commands. Do this multiple times and there is always one time you forgot one thing.
  • As a team you have to organize where to store this work-procedure and how to share secrets (like passwords) with each other.
  • Because above is not a trivial task, deployments probably won’t happen every time a developer commits a change. Wait too long and your environments get out-of-sync.

This is nothing new and it is not specific to Drupal 8 either.
It can be solved by automating deployments. This process is named continuous integration.

Simple Git and Drush Drupal 8 deployment procedure

What are we going to do:

  1. After each commit, we trigger GitLab CI to perform some actions
  2. We let GitLab CI login on the hosting-server via SSH
  3. On the hosting server we run several commands like $ git pull and $ drush config-import

If a job fails, GitLab CI will send an e-mail.
If a job is successful, the newest commits are deployed within minutes.

To continue this tutorial you already must have steps 2 & 3 up and running.

Pre requisites

  1. You have a GitLab account
  2. You have created a GitLab project and pushed a Drupal project to it
    1. We use https://github.com/drupal-composer/drupal-project but it is not needed for this tutorial
  3. You have SSH access to your hosting server
  4. On the hosting server you have installed
    1. Git
    2. Composer
    3. Drush
  5. You can run ‘$ git pull’ on your hosting-server (in GitLab you must add an Deploy Key in Settings > Repository).

So the pre-requisite is that you can deploy manually .

Step 1: Create SSH public and private key so GitLab can login on the hosting server

Gitlab CI uses SSH to login on your hosting server.

This is of course security related. If you have doubts please let youself be adviced by an expert.

  • Let’s assume you login with a specific user ‘gitlabuser’ and your domain is ‘example.com’. Now you must add the SSH public key that GitLab CI will be using to the `gitlabuser’ account on the hosting-server.
  • You can choose to not create a new account for the gitlabuser and instead add the SSH public key to the ‘authorized_keys' list of an existing user.
  • Do not re-use existing SSH-keys from existing users (for example your own developer-laptop), but create a new keypair.

Store and distribute SSH-keys carefully with tools like Lastpass or 1Password.

First create a new SSH keypair locally

  1. https://docs.gitlab.com/ee/ssh/#generating-a-new-ssh-key-pair

Now add the public key to the user-account on the hosting server

$ cat id_rsa.pub | ssh gitlabuser@example.com 'cat >> .ssh/authorized_keys'

At last add the private key to GitLab

  1. Go to your GitLab repository.
  2. In the left hand menu go to “Settings”, then “CI / CD”, then “Settings”.
  3. Scroll down to Variables and add a new one.
  4. Input variable key: “SSH_PRIVATE_KEY”
  5. Input variable value: the private key value (copy paste from a text-editor)
  6. Save variables.

Note: Do not make the variable “protected”, because then the GitLab CI runner can’t access it.
Note: Only team-members with owner or maintainer permission have access to the settings page in GitLab.

Step 2: Commit .gitlab-ci.yml script to your repository

This script only runs when you commit to master.

You can fork this script here: https://gitlab.com/dx-experts/drupal-templates-for-gitlab-ci

# GitLab CI script for Drupal 8 websites.
# @see https://www.dx-experts.nl/blog/2018/drupal-8-and-gitlab-ci-setup-guide
# @see https://gitlab.com/dx-experts/drupal-templates-for-gitlab-ci
#
# This script deploys a Drupal 8 site on each commit on the master-branch.
#
# Preparation:
# Save $SSH_PRIVATE_KEY first in GitLab CI variables.
variables:
  GIT_ROOT: "/var/www/my-website/"
  SSH_LOGIN: "ssh -p22 -oStrictHostKeyChecking=no gitlabuser@example.com"
  DRUSH: "drush"
  COMPOSER: "composer"
before_script:
# @see https://docs.gitlab.com/ee/ci/ssh_keys/
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
deploy_master:
  when: always
  only:
  - master
  script:
  - $SSH_LOGIN "cd $GIT_ROOT && git fetch --tags"
  - $SSH_LOGIN "cd $GIT_ROOT && git pull"
  # Never blindly run composer update on the server: https://blog.martinhujer.cz/17-tips-for-using-composer-efficiently/
  - $SSH_LOGIN "cd $GIT_ROOT && $COMPOSER install"
  # Updates must be run before configuration is imported
  # https://drupal.stackexchange.com/questions/188840/what-order-should-configuration-import-and-module-updates-be-run
  - $SSH_LOGIN "cd $GIT_ROOT && $DRUSH updatedb -y"
  - $SSH_LOGIN "cd $GIT_ROOT && $DRUSH config-import -y"
  # (Additional) sanitizing tasks.
  #- $SSH_LOGIN "cd $GIT_ROOT && $DRUSH locale:check"
  #- $SSH_LOGIN "cd $GIT_ROOT; $DRUSH locale:update"
  - $SSH_LOGIN "cd $GIT_ROOT && $DRUSH core-cron"
  # You should consider fine-grained cache invalidation
  # @see https://www.dx-experts.nl/blog/2017/drupal-8-development-caching
  - $SSH_LOGIN "cd $GIT_ROOT && $DRUSH cache-rebuild -y"

What is happening???

Variables

GIT_ROOT variable: this is the folder where the Drupal website is installed on the hosting-server.
SSH_LOGIN variable is used as SSH login string.
-p22 is the ssh-portnumber (22 is default).
-oStrictHostKeyChecking=no disables the ‘trust this host’ question by default visible the first time one creates an ssh connection to a server.

Before script

See https://docs.gitlab.com/ee/ci/ssh_keys/

Basically, it assures that the SSH-agent and SSH_PRIVATE_KEY are available to the GitLab-CI runner.

Step 3: Let’s run the deployment script

Every time you push a commit to the master branch the above script will run.

  • It’s called a job and you can find its progress in the GitLab UI: left sidebar menu “CI / CD” > “Jobs”.
  • The newest commit will be pulled on the hosting server.
  • Composer installs what you declared.
  • Drush updates the site and import new config.

You commit is now deployed! Yay!

Debugging

If you have problems with accessing the server from GitLab CI try adding –-verbose flags to the SSH_LOGIN variable.

  • Double check if you can login, without password, with the public/private-key pair.
  • Especially if you have not used Drupal Composer Project as template for your Drupal 8 site you should double check the GIT_ROOT variable.
  • When you login on the server yourself you must be able to run all commands in the deploy_prod script manually without errors. Add –-verbose tags those commands that fail for further investigation.
  • When using Fish-command-line replace && with ; to separate commands.
  • Use an absolute path to the Drush or Composer executables
    DRUSH: "~/.composer/vendor/bin/drush"
    COMPOSER: "php ~/.composer/composer.phar"
    @see https://askubuntu.com/questions/810098/why-doesnt-my-alias-work-over-ssh

Feel free to create an issue in the GitLab repository with the template we presented above.

More advanced deployment procedures

Continuous integration can be made as advanced as needed. The approach written down above lacks a lot of advanced features.

Examples of advanced deployment features:

  • A unique environment per git-branch (every git-branch has its own environment with an own URL, etc. Platform.sh has this architecture and it’s great for projects with lots of changes and feature-branches)
  • A green/red deployment procedure (where a build will be tested first with PHPUnit or Cypress, and only if those tests are green the build will be deployed)
  • An intermediate artifact step (where assets are fetched and runtimes are compiled)

If you have any needs regarding advisory or implementation of above workflows don’t hesitate to contact Dx Experts.

Author Niels de Feyter