ansible-lint on gitlab-ci

February 24, 2021

As ansible-lint checks playbooks and roles for common problems and possible improvements we will explore how to run the linter on top of Gitlab CI.

I prefer containers as CI tasks should produce idempotent results. This way one can use different linting runtimes on many projects while offering great flexibility without modifying the base OS. As ansible-lint is written in Python we need a python-runtime. The resulting linter image can be reused and therefore the build time decreases drastically. The gitlab docs describe the runner installation and registration process in depth.

A first approach

By using the python3 slim base image one can define a basic gitlab CI-configuration like this:

.gitlab-ci.yml – draft, do not use

image: python:3-slim

before_script:
  - pip install ansible-lint
  - ansible-lint --version

stages:
  - ansible-lint

ansible-lint:
  stage: ansible-lint
  script:
    - ansible-lint site.yml

Not bad, but this approach has two problems:
As the version of ansible-lint is still unconfigured package updates could cause issues you do not want to deal with regularly. The base image is updated very frequently. So by not configuring any base image version this would create a lot of different images wasting precious disk space.

Creating a custom docker image

I’ve based my docker image on the popular python3 slim image available on docker hub (running debian buster under the hood). So let’s create a docker image.

Dockerfile

FROM python:3-slim

ENV ANSIBLE_VERSION="2.10.6."
ENV ANSIBLE_LINT_VERSION="4.3.7."

RUN pip install ansible-lint==$ANSIBLE_LINT_VERSION ansible==$ANSIBLE_VERSION

As you can see both versions are configured by environment variables while I keep the ansible version always in sync with my locally used version. I’m using a private docker registry so whenever this Dockerfile changes it’s rebuilt by the CI and pushed again.

docker image .gitlab-ci.yml

build:
  stage: deploy
  tags:
    - shell
  script:
    - export BUILD_VERSION=$(date +%s)
    - docker build -t your.registry.example.org/dynpages/ansible-lint:$BUILD_VERSION .
    - docker tag your.registry.example.org/dynpages/ansible-lint:$BUILD_VERSION your.registry.example.org/dynpages/ansible-lint:latest
    - docker push your.registry.example.org/dynpages/ansible-lint:$BUILD_VERSION
    - docker push your.registry.example.org/dynpages/ansible-lint:latest

The image is always tagged twice, by a timestamp and as latest version. The final gitlab-CI configuration I ended up looks like this:

.gitlab-ci.yml

image: your.registry.example.org/dynpages/ansible-lint:latest

stages:
  - ansible-lint

ansible-lint:
  stage: ansible-lint
  script:
    - ansible-lint -L
    - ansible-lint -r ./ site.yml

ansible-lint -L is executed first to list all available rules.

Photo by Sam Moqadam on Unsplash

Leave a comment