Testing Plone projects with Gitlab-CI

How to run continuous integration on private Plone projects, for free.

Continuous integration testing is a key enabling practice for agile coding projects. In the Plone community, it’s become best practice to combine a Github code repository with a Travis-CI testing integration for separate packages. In addition, the Plone CI team uses Jenkins to test the overall Plone integration.

Both Github and Travis are free for public open source projects, but running private projects on Travis is an expensive proposition. Jenkins can be hard to set up and maintain, and the user interface is becoming long in the tooth.

Enter Gitlab and Gitlab-CI. Gitlab shamelessly replicates most of the Github user experience. But, in contrast to Github, private repositories are for free on Gitlab. In addition, Gitlab-CI offers Continuous Integration features for free also.

For a new Quaive customer project, I needed a private repository with continuous integration testing. Below I’m sharing with you how you can set up your private Plone projects on Gitlab-CI, too.

Overview

These are the steps involved to make this work:

  1. Create an account on Gitlab.
  2. Create a repo and push your current code base.
  3. Prepare a Docker image to use as a base for Gitlab-CI testing.
  4. Prepare your code for Gitlab-CI testing.
  5. Configure, start and attach Gitlab-CI test runners.
  6. Push your work on any branch and check test results.

You can figure out the first two steps on your own.

If you want to use Gitlab as a second repository and pipeline, in addition to e.g. an existing Github origin, you can simply add another remote and push there:

git remote add gitlab <url>
git push gitlab master

Docker rules

If you’re still afraid of Docker, now is the time to overcome that fear. The way Gitlab-CI uses docker is awesome and a gamechanger for testing. Here’s the benefits I’m enjoying:

Prepare a Docker image to use as a base for Gitlab-CI testing

You have to, once, prepare a Docker image with all system dependencies and an eggs cache. That image will be cached locally on your CI runners and used as a base for every test run.

I use a Docker image with a fully primed cache:

Note that Quaive is quite a complex code base. For a less complex Plone project you could prune the list of installed system packages and eggs. YMMV.

Prepare your code for Gitlab-CI testing

You simply add a .gitlab.yml script that configures the test runs. This is required and the file must have that name.

In addition, it makes sense to add a specialized gitlab.cfg buildout file, but this is not required and the name is completely free.

For those of you who have worked with .travis.yml before, this will look very similar:


before_script:
  - export LC_CTYPE=en_US.UTF-8
  - export LC_ALL=en_US.UTF-8
  - export LANG=en_US.UTF-8
  - virtualenv --clear -p python2.7 .
  - bin/pip install -r requirements.txt
  - bin/buildout -c gitlab-ci.cfg
  - /etc/init.d/redis-server start
  - Xvfb :99 1>/dev/null 2>&1 &

robot:
  script:
    - DISPLAY=:99 bin/test -t 'robot'

norobot:
  script:
    - bin/code-analysis
    - bin/test -t '!robot'
    - ASYNC_ENABLED=true bin/test -s ploneintranet.async

Here’s what’s happening:

So this defines two CI runners, with a shared setup fixture that is used in both.

Configure, start and attach Gitlab-CI test runners

If you followed the above closely, you’ll notice that the Docker image we configured has not been referenced yet. That follows in this step, which brings everything together.

On your Linux workstation or server you want to use as a host for running the CI tests, first install Docker:


sudo apt-get install docker.io

Then, start a runner in auto-reboot mode:


docker run -d --name gitlab-runner-01 --restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /srv/gitlab-runner-01/config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest

If you want to have multiple runners in parallel, repeat the above, replacing gitlab-runner-01 with gitlab-runner-02 etc.

Now go to your Gitlab account. Navigate to your project. In the sidebar, choose “Settings”. Scroll down the sidebar and choose “Runners”.

First disable the shared runners provided by Gitlab. They’re not suitable for testing your Plone project.

Now, while keeping this screen open (You’ll need to copy the registration token), go to your Linux terminal again and register the runner you started above, as a runner for this Gitlab project:


docker exec -it gitlab-runner-01 gitlab-runner register

This will involve you in something like the following dialog:


Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/ci )
https://gitlab.com/ci
Please enter the gitlab-ci token for this runner
xxx copy from Gitlab settings xxx
Please enter the gitlab-ci description for this runner
myhost-runner-01
INFO[0034] fcf5c619 Registering runner... succeeded
Please enter the executor: shell, docker, docker-ssh, ssh?
docker
Please enter the Docker image (eg. ruby:2.1):
yourorganisation/yourproject:latest
INFO[0037] Runner registered successfully.

Here in the last question you use the name of the Docker image you pushed to Docker hub before.

Repeat the registration process in case you want to add multiple runners.

That concludes the hard part.

Push your work on any branch and check test results

Now, any push to any branch on your Gitlab project will trigger the builds you configured. You can see them in the sidebar of your project under “builds” and you can follow the console of the build as it’s progressing.

What I like about this build system is the “Retry build” button. No more pleading with Jenkins on Github comments to try and trigger the pull request builder (which happens to ignore the trigger phrases you configured because it always only uses it’s own hardcoded triggers).

Also, you don’t need to open a fake pull request just to trigger a build. So annoying to open a pull request overview on Github and see lots of outdated, broken builds which are not really pull requests but just a hack to get Jenkins going. No more.

Gotchas

There’s two gotchas you need to be aware of here:

Conclusion

On Quaive, we now use Jenkins-CI with Github pull request integration, in parallel with the Gitlab-CI setup described above. This makes it very easy to triangulate whether a test failure is caused by code or by the test server. It also provides us with both the benefits of per-branch per-push Gitlab-CI testing and the virtual merge pull request testing of Gitlab + Jenkins.

If you’re interested in setting this up for your own projects and running into issues, just start a thread on community.plone.org and ping me (@gyst), or leave a comment below this post.