Run your linters inside docker

Running your development enviroment inside docker, instead of running it directly on your laptop, come with many advantages. For example, it allow you to run your tests in the same conditions as your automized tests (continuos integration) that runn on Travins or Gitlab-Ci, so that if it works there, it will work on your local machine. The problem, to use it on your development enviroment, is that many IDE’s was not made to run commands and linters inside docker contaienrs. In this tutorial, we will see how this problem can by solved.

There are diferent options

When one try to solve a problem, then there are normally more than one option to solve it. Two of them are the following:

Use docker exec to run your linters inside docker.

When we work with docker-compose, we normally use `docker-compose up` or `docker-compose run …`. Bouth will start a container. So if you run `docker ps`, you will get something like this:

We can run a command in a running container. For example to run rubocop (rails/ruby linter) inside a container, we can do the following:

Now, we could just execute in one terminal docker-comose up and in the other one docker-compose exec app bash (substitute app with your service name)

Looks easy or not? Unafortunity, this aproach still present one problem. The problem is that, normally, in the docker-compoye.yml a default command is specified, so that the service automaticly starts on docker-compose up. This is normally not a problem, untill you want to develop on this service, then it come very frustraiting when you have to press CTR-C to stop docker-compose and finishing in the same tame, all the docker-compose exec processes. I normally by creating a docker-compose.overwrite.ymlwhere I overwrite the default comand with a sleep or something similary. It is also a good place to put custom configurations.

# file name: docker-compose.overwrite.yml

version: '2.2'
services:
  # substitute app with your main service
  app:
    # Sometimes, there are better comands like sleep.
    # For example, in rails applications, I prefer
    # bash -c "spring server || sleep 10000000"
    command: sleep 10000000
    # HACK: some linter use absolute paths instead relative (e.g. rubocop-linter on recent versions of atom).
    # If this is your case, then you need the following lines. I have testet the hack on
    # MAC and linux but not on windows
    workdir: $pwd
    volumes:
      .:$pwd

Note: Do not forget to add it to your .gitignore (local or global). 

Some linters, like rubocop-atom, fail when we put docker-compose exec <your service> <linter> with a message like The input device is not a TTY. Some times this can be solved by using docker-compose exec -T <your service> <linter>but this does not allwas work. What allways work is:

docker exec -i <container name> <linter>

You can get the name of the container image by typing docker ps.

With this, we can allright user our ide linters, but it is still not very nice, becuase if we work on multiple projects, then we have to edit the comands, every time we change from one to other project. To solve this, let us create a smal script that we can add to our global .gitignore

#!/bin/sh

# File name: docker-linter-exec.sh

dcoker exec -i <container name> ${@:1}

Give it execution permissions

chown a+xrw docker-linter-exec.sh

Configure your IDE

Because every IDE is different, there exist no universal tutorial for this part. Here I will demostrate, how It can be done with atom and his rubocop plugin.

First, we need open preferences:

Second, we have to go to the plugin (E.g rubocop)

Last, we have to set the command