/hadolint

Dockerfile linter written in Haskell

Primary LanguageHaskellGNU General Public License v3.0GPL-3.0

Haskell Dockerfile Linter Build Status GPL-3 licensed

pipecat

Try it out online: http://hadolint.lukasmartinelli.ch/

A smarter Dockerfile linter that helps you build best practice Docker images. The linter is parsing the Dockerfile into an AST and performs rules on top of the AST. It is standing on the shoulders of Shellcheck to lint the Bash code inside RUN instructions.

Screenshot

How to use

On the web

The best way to use hadolint is using it online on http://hadolint.lukasmartinelli.ch/.

From your terminal

If you have hadolint installed locally you can run it on any local Dockerfile to get the lint results.

hadolint <dockerfile>

With Docker

Docker comes to the rescue to provide an easy way how to run hadolint on most platforms. Just pipe your Dockerfile to docker run:

docker run --rm -i lukasmartinelli/hadolint < Dockerfile

Install

If you are on OSX you can use brew to install hadolint.

brew install hadolint

You can also build hadolint locally. You need Haskell and the stack build tool to build the binary.

git clone https://github.com/lukasmartinelli/hadolint
cd hadolint
stack build

Rules

Incomplete list of implemented rules. Click on the error code to get more detailed information.

  • Rules with the prefix DL originate from hadolint. Take a look into Rules.hs to find the implementation of the rules.
  • Rules with the SC prefix originate from ShellCheck (Only the most common rules are listed, there are dozens more)

Please create an issue if you have an idea for a good rule.

Rule Description
DL3000 Use absolute WORKDIR.
DL3001 For some bash commands it makes no sense running them in a Docker container like ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig.
DL3002 Do not switch to root USER.
DL3003 Use WORKDIR to switch to a directory.
DL3004 Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root.
DL3005 Do not use apt-get upgrade or dist-upgrade.
DL3007 Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag.
DL3006 Always tag the version of an image explicitly.
DL3008 Pin versions in apt get install.
DL3009 Delete the apt-get lists after installing something.
DL3010 Use ADD for extracting archives into an image.
DL3011 Valid UNIX ports range from 0 to 65535.
DL3012 Provide an email adress or URL as maintainer.
DL3013 Pin versions in pip.
DL3014 Use the -y switch.
DL3015 Avoid additional packages by specifying --no-install-recommends.
DL3020 Use COPY instead of ADD for files and folders.
DL4000 Specify a maintainer of the Dockerfile.
DL4001 Either use Wget or Curl but not both.
DL4003 Multiple CMD instructions found.
DL4004 Multiple ENTRYPOINT instructions found.
SC1000 $ is not used specially and should therefore be escaped.
SC1001 This \c will be a regular 'c' in this context.
SC1007 Remove space after = if trying to assign a value (or for empty string, use var='' ...).
SC1010 Use semicolon or linefeed before done (or quote to make it literal).
SC1018 This is a unicode non-breaking space. Delete it and retype as space.
SC1035 You need a space here
SC1045 It's not foo &; bar, just foo & bar.
SC1065 Trying to declare parameters? Don't. Use () and refer to params as $1, $2 etc.
SC1066 Don't use $ on the left side of assignments.
SC1068 Don't put spaces around the = in assignments.
SC1077 For command expansion, the tick should slant left (` vs ´).
SC1078 Did you forget to close this double quoted string?
SC1079 This is actually an end quote, but due to next char it looks suspect.
SC1081 Scripts are case sensitive. Use if, not If.
SC1083 This {/} is literal. Check expression (missing ;/\n?) or quote it.
SC1086 Don't use $ on the iterator name in for loops.
SC1087 Braces are required when expanding arrays, as in ${array[idx]}.
SC1095 You need a space or linefeed between the function name and body.
SC1097 Unexpected ==. For assignment, use =. For comparison, use [/[[.
SC1098 Quote/escape special characters when using eval, e.g. eval "a=(b)".
SC1099 You need a space before the #.
SC2002 Useless cat. Consider `cmd < file
SC2015 Note that `A && B
SC2026 This word is outside of quotes. Did you intend to 'nest '"'single quotes'"' instead'?
SC2028 echo won't expand escape sequences. Consider printf.
SC2035 Use ./*glob* or -- *glob* so names with dashes won't become options.
SC2046 Quote this to prevent word splitting
SC2086 Double quote to prevent globbing and word splitting.
SC2140 Word is on the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?
SC2154 var is referenced but not assigned.
SC2164 Use `cd ...

Develop

This is my first Haskell program. If you are a experienced Haskeller I would be really thankful if you would tear my code apart in a review.

Setup

  1. Clone repository
    git clone --recursive git@github.com:lukasmartinelli/hadolint.git
    
  2. Create a new sandbox.
    cabal init
    
  3. Install the dependencies
    cabal install --only-dependencies
    

REPL

The easiest way to try out the parser is using the REPL.

cabal repl

In the REPL you can load the parser code with :l Parser.hs and use parseString or parseFile to get a quick look at the AST.

parseString "FROM debian:jessie"

Tests

Run unit tests.

cabal test

Run integration tests.

./integration_test.sh

Parsing

The Dockerfile is parsed using Parsec and is using the lexer Lexer.hs and parser Parser.hs.

AST

Dockerfile syntax is is fully described in the Dockerfile reference. Just take a look at Syntax.hs to see the AST definition.

Alternatives