Lint checking is an extremely effective aid for maintaining the quality of your code, and it is now easy to set up the same quality checking on both your development and continuous integration systems.
In this post we will cover linting with the Atom text editor, and also look at enforcing lint checks in the build process with Travis CI, and setting up Code Climate to give us project analysis with a nice user interface. Code Climate runs a suite of Open Source linters, so that we can apply the same linter rules in all of these environments.
Travis and Code Climate themselves are proprietary services, but there is no fee for Open Source projects. To self-host your Continuous Integration and code analysis, try Jenkins with either linters or Sonarqube.
So, Umm, What is a Linter?
A linter is a command-line tool that checks one or more files that have been written in a particular programming language or data format against a set of rules. Every programming language has best practices and a common set of style rules that are either endorsed by the core team, or generally accepted by the community, and these are coded into the linter. If a line in a file does not meet one of these rules, the linter prints outs the file and line number where the problem was found, and the rule that has been broken.
Some linters also provide an option to automatically rewrite your code to fix issues that they find. Automatic code reformatting is definitely outside the scope of this introductory post.
Unlike unit tests, lint checks are fast enough that they can effectively be instant (as far as humans can tell). This means that these checks can be integrated everywhere. Linters will run interactively with your editor, so that you can get feedback on screen as your write your code. You can easily set your Continuous Integration system to run lint checks along with your test suites, without adding any significant delay to the build time. Extra services like Code Climate use suites of linters to rapidly provide detailed code analysis on your projects for every Git commit.
A Handy List of Linters
- C and C++ - Linter: Various, or use your compiler!, Atom package: linter-clang* or linter-gcc*
- CoffeeScript - Linter: CoffeeLint, Atom package: linter-coffeelint
- CSS - Linter: CSSLint, Atom package: linter-csslint
- English (see below!) - Linter: Proselint, Atom package: linter-proselint*
- Go - Linter: golinter (style-only) and gometalinter (all the things), Atom package: linter-golinter* or go-plus*
- HAML - Linter: haml-lint, Atom package: linter-haml*
- Java - linting is built-in, Atom package: linter-javac
- PHP - linting is built-in, Atom package: linter-php
- Python - Linter: Pylint, Atom package: linter-pylint*
- Ruby - Linter: Rubocop, Atom package: linter-rubocop*
- Rust - Basic linting is built-in but there’s also Clippy, Atom package: linter-rust
- SASS - Linter: sass-lint, Atom package: linter-sass-lint*
- XML - Linter: xmllint, Atom package: linter-xmllint*
- YAML - Linter: yaml-js, Atom package: linter-js-yaml
The asterisk means that the Atom linter package for that language requires the separate command-line linter utility to be installed. Check the Web page for the Atom package, which will explain.
Automatic Linting in Atom
To use a particular linter with the Atom editor, install the appropriate package. I prefer to install multiple Atom packages from the command-line, simply because it is quicker to copy and paste the command. The apm utility that is supplied with Atom makes this easy:
apm install linter-csslint linter-eslint linter-rust
Otherwise, the packages for Atom rely on you installing the command-line linter separately. For example, if you are a Ruby developer, either include the Rubocop linter in your project by adding it to the Gemfile, or add it to your Ruby installation, so that one version will be available to all projects.
To include Rubocop in the Gemfile, add it to the development and test groups, with require set to false:
group :development, :test do gem 'rubocop', require: false end
To add Rubocop to your Ruby installation with RubyGems:
gem install rubocop
Use the helpful option to generate initial configuration files:
Finally, enter this command to install the Atom package:
apm install linter-rubocop
Once you add linter packages in Atom, the editor runs a linter each time that you open or save a file that is relevant to whichever installed linter packages. If there are any linter warnings for an open file, the bottom of the file tab shows a list of linter warnings. The number of issues for the current file and the total number of linter issues for the project also appear in the bar at the bottom of the Atom window.
Incidentally, you will notice that Atom has a package that is just called linter, and is labelled A Base Linter with Cow Powers. This is the framework that all of the actual linter packages for various programming languages use, and it is installed the first time that you add any linter package to Atom.
Tuning Your Linters
Once you have started using linters, you will quickly realise that you do not always agree with what they tell you. Sometimes there will be parts of your code where you deliberately break rules, or standard rules that you do not agree with. For this reason, each linter will check the root directory of your project for a configuration file that overrides the defaults, and have ways of adjusting or disabling rules for particular files.
Each linter works differently, so it is hard to generalise beyond this. For a good example of a linter, take a look at the documentation for ESLint, which fully explains the rules, how to configure them, how to exclude specific files, and how to disable individual rules for a particular file.
By the way, if you use Code Climate, the setup process generates a set of configuration files for the default linters, so that you can download and add these to your project immediately.
Adding Lint Checking to Your Continuous Integration
Consider using linters to provide a pass or fail check for commits, so that if the linters finds any issue, the test fails. This enables you to enforce coding standards for the project, since contributors must specifically exclude every issue that they do not intend to fix.
Your CI system marks a build as failed if any step in the build process raises an error. Linters will raise an error if they find any issue, so to use a linter to check commits, you simply need to modify your CI build process to run that linter in the same way that it runs the unit tests. There are three steps to do this:
Make sure that the build environment will have the linters installed. Either add this to the job configuration for the CI service, or include the linters as dependencies for the build process.
Add the configuration files for your linters to the repository for your project.
Edit your build process to run the linters.
I used three resources to help me set this up:
- The Travis CI documentation
- Travis for Node.js developers by dwyl
- Rubocop and Rails: Getting started, by Joan E. Hughes
Project Analysis with Code Climate
For larger projects, especially projects that have been in development for a long time before you started linting, it may more be practical to just check analysis reports and fix issues over time, rather than fail commits that do not meet the standard.
You may also find that the analysis that is provided by a third-party service has more features. For example, Code Climate uses multiple linters, and also runs additional quality checks like Brakeman, Bundler-audit and test coverage.
Rather than repeat the setup documentation for Code Climate, I will just provide a link.
Once you have set up a project, you must also add a webhook to your repository host. This triggers a new analysis each time that a commit is pushed.
Remember to set up an integration with your preferred chat service as well! For example, this page explains how to set up Slack. Code Climate will send messages whenever an analysis considers that a file in the project is Grade D or worse.
Bonus Fun: Linting Prose
You can lint your writing as well as your code! (English-only, unfortunately). Proselint tests plain-text files like Markdown for an interesting miscellany of problems. These include stray punctuation and FIXMEs, as well as clichés, jargon, and other offences against the English language.
Like Rubocop, Proselint requires you to have support for a particular programming language installed on your computer. To use Proselint, you will need Python 2 or above, and Pip, the Python package manager.
On macOS, you will have Python 2, but will need to install Pip yourself first. Enter this command in a terminal window:
easy_install --user pip
Then add this to the $PATH for your user account:
Fedora and Ubuntu include both Python and Pip by default, so you do not need to do either of the previous steps on these Linux systems.
To install the Proselint command-line tool with Pip, enter this in a terminal window:
pip install --user proselint
You can then install a plugin for your text editor. To integrate Proselint into Atom, add the linter-proselint package. If you use the command-line, the APM command is:
apm install linter-proselint