Auto code style comments with Gitlab and Pronto/Rubocop

24th March 2017 – 810 words

With Hound, there is already an easy way to include automatic code style comments into Github projects. We recently introduced something comparable into our (self-hosted) Gitlab CI pipeline. The heavy work is done be the excellent Pronto, a pluggable code style linter which uses battle-tested linter under the hood like Rubocop, Eslint, phpcs and many more.

Creating Docker image with embedded code style tools

As we are using Docker for running our pipeline, we created a new Docker image that we deploy to the runner nodes. That image just has all the tools included that we need for testing, as well as the various linter default config files that we are using across all projects.

First, create private token in your (or the account that you want to use for commenting) Gitlab profile under Profile/Settings -> Access Tokens.

FROM ruby:2.4
MAINTAINER Your Name <info@your.domain.com>

# env variables for 'gitlab' executable for test
ENV GITLAB_API_PRIVATE_TOKEN YourSecretGitlabToken-209xas
ENV GITLAB_API_ENDPOINT https://git.yourcompanyserver.com/api/v3

# cmake is required by pronto
RUN apt-get update -y && \
      apt-get install -y cmake

RUN gem install pronto \
	# just list all the linters you are planning to use
	pronto-brakeman pronto-slim pronto-scss pronto-rubocop \
	--no-ri --no-rdoc

RUN echo "Testing Gitlab API access.." && \
      gitlab projects "{ per_page: 1000, page: 1 }" --only=id,path_with_namespace

ENV PRONTO_GITLAB_API_PRIVATE_TOKEN $GITLAB_API_PRIVATE_TOKEN
ENV PRONTO_GITLAB_API_ENDPOINT $GITLAB_API_ENDPOINT

# copy over various default linter files
COPY rubocop.yml /root/.rubocop.yml
COPY scss-lint.yml /root/.scss-lint.yml

Here, rubocop as well as scss-lint are using default style files under the current user’s home directory. Any project might override these by placing a project specific file into the project folder. Feel free to add more pronto-runner besides the one for Rubocop, Slim, Scss or Brakeman.

Now, build the container:

docker build -t yourcompany/code-style:v1 .

Roll out the docker image to the runner nodes or rerun the script above on each runner node, depends on your setup.

Add the pronto task to your .gitlab-ci.yml

Add or modify your .gitlab-ci.yml in the project and commit it.

# project .gitlab-ci.yml

states:
  - test

pronto:
  image: yourcompany/code-style:v1
  stage: test
  script: |
    export PRONTO_GITLAB_SLUG="$CI_PROJECT_ID"
    pronto run -f gitlab

Done! Push & deploy and your commit log should fill up with commit messages.

Appendix 1: Rubocop hints

Start by installing a recent Rubocop version with gem install rubocop. Run rubocop app -D and have a look at the output. You probably want to disable or cool down a lot of “Cops”, so by running with -D the Cop name is listed alongside every check.

  • rubocop -f w shows you the files with the most style/linter problems
  • rubocop -f o shows you the list of all Cops Rubocop output
  • rubocop -D --only Style/SpaceInsideParens,Style/AlignHash,Style/HashSyntax,Style/SpaceInsideBlockBraces -a Autocorrect the given list of Cops - One of the best features of Rubocop is to automatically. Run with caution and commit often to be able to go back, if you didn’t like the result.

Here an example Rubocop.yml you might take as a starting point:

AllCops:
  TargetRubyVersion: 2.4
  Exclude:
    - 'bin/*'
    - 'scripts/**/*'
    - 'db/**/*'
Rails:
  Enabled: true
Style/GuardClause:
  MinBodyLength: 1
Style/AndOr:
  EnforcedStyle: conditionals
Style/PercentLiteralDelimiters:
  PreferredDelimiters: {}
Style/Alias:
  Enabled: false
Style/Documentation:
  Enabled: false
Style/PercentLiteralDelimiters:
  Enabled: false
Lint/UnusedMethodArgument:
  Enabled: false
Rails/HasAndBelongsToMany:
  Enabled: false
Metrics/ModuleLength:
  Enabled: false
Style/StringLiterals:
  Enabled: false
Style/IfUnlessModifier:
  Enabled: false
Metrics/MethodLength:
  Enabled: false
Metrics/BlockLength:
  ExcludedMethods: ['task', 'namespace', 'get', 'post', 'describe', 'context', 'specify', 'it', 'route_param', 'draw']

Appendix 2: Integrating into vim

Fortunately, with Syntastic plugin very easy, but you need to explicitly include it like:

let g:syntastic_ruby_checkers = ['mri', 'rubocop']

This will run both the (default) Ruby built-in lint and Rubocop afterwards.