GithubHelp home page GithubHelp logo

hadolint / hadolint Goto Github PK

View Code? Open in Web Editor NEW
9.7K 65.0 391.0 3.82 MB

Dockerfile linter, validate inline bash, written in Haskell

License: GNU General Public License v3.0

Haskell 95.58% Shell 1.63% Dockerfile 0.14% Nix 2.65%
dockerfile linter shellcheck haskell dockerfile-linter docker static-analysis

hadolint's Introduction

Haskell Dockerfile Linter

Build Status GPL-3 licensed GitHub release GitHub downloads pipecat

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

🌐 Check the online version on hadolint.github.io/hadolint Screenshot

Table of Contents

How to use

You can run hadolint locally to lint your Dockerfile.

hadolint <Dockerfile>
hadolint --ignore DL3003 --ignore DL3006 <Dockerfile> # exclude specific rules
hadolint --trusted-registry my-company.com:500 <Dockerfile> # Warn when using untrusted FROM images

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

docker run --rm -i hadolint/hadolint < Dockerfile
# OR
docker run --rm -i ghcr.io/hadolint/hadolint < Dockerfile

or using Podman:

podman run --rm -i docker.io/hadolint/hadolint < Dockerfile
# OR
podman run --rm -i ghcr.io/hadolint/hadolint < Dockerfile

or using Windows PowerShell:

cat .\Dockerfile | docker run --rm -i hadolint/hadolint

Install

You can download prebuilt binaries for OSX, Windows and Linux from the latest release page. However, if this does not work for you, please fall back to container (Docker), brew or source installation.

On OSX, you can use brew to install hadolint.

brew install hadolint

On Windows, you can use scoop to install hadolint.

scoop install hadolint

On distributions that have nix installed, you can use the hadolint package to run ad-hoc shells or permanently install hadolint into your environment.

As mentioned earlier, hadolint is available as a container image:

docker pull hadolint/hadolint
# OR
docker pull ghcr.io/hadolint/hadolint

If you need a container with shell access, use the Debian or Alpine variants:

docker pull hadolint/hadolint:latest-debian
# OR
docker pull hadolint/hadolint:latest-alpine
# OR
docker pull ghcr.io/hadolint/hadolint:latest-debian
# OR
docker pull ghcr.io/hadolint/hadolint:latest-alpine

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

git clone https://github.com/hadolint/hadolint \
  && cd hadolint \
  && cabal configure \
  && cabal build \
  && cabal install

If you want the VS Code Hadolint extension to use Hadolint in a container, you can use the following wrapper script:

#!/bin/bash
dockerfile="$1"
shift
docker run --rm -i hadolint/hadolint hadolint "$@" - < "$dockerfile"

CLI

hadolint --help
hadolint - Dockerfile Linter written in Haskell

Usage: hadolint [-v|--version] [-c|--config FILENAME] [DOCKERFILE...]
                [--file-path-in-report FILEPATHINREPORT] [--no-fail]
                [--no-color] [-V|--verbose] [-f|--format ARG] [--error RULECODE]
                [--warning RULECODE] [--info RULECODE] [--style RULECODE]
                [--ignore RULECODE]
                [--trusted-registry REGISTRY (e.g. docker.io)]
                [--require-label LABELSCHEMA (e.g. maintainer:text)]
                [--strict-labels] [--disable-ignore-pragma]
                [-t|--failure-threshold THRESHOLD]
  Lint Dockerfile for errors and best practices

Available options:
  -h,--help                Show this help text
  -v,--version             Show version
  -c,--config FILENAME     Path to the configuration file
  --file-path-in-report FILEPATHINREPORT
                           The file path referenced in the generated report.
                           This only applies for the 'checkstyle' format and is
                           useful when running Hadolint with Docker to set the
                           correct file path.
  --no-fail                Don't exit with a failure status code when any rule
                           is violated
  --no-color               Don't colorize output
  -V,--verbose             Enables verbose logging of hadolint's output to
                           stderr
  -f,--format ARG          The output format for the results [tty | json |
                           checkstyle | codeclimate | gitlab_codeclimate | gnu |
                           codacy | sonarqube | sarif] (default: tty)
  --error RULECODE         Make the rule `RULECODE` have the level `error`
  --warning RULECODE       Make the rule `RULECODE` have the level `warning`
  --info RULECODE          Make the rule `RULECODE` have the level `info`
  --style RULECODE         Make the rule `RULECODE` have the level `style`
  --ignore RULECODE        A rule to ignore. If present, the ignore list in the
                           config file is ignored
  --trusted-registry REGISTRY (e.g. docker.io)
                           A docker registry to allow to appear in FROM
                           instructions
  --require-label LABELSCHEMA (e.g. maintainer:text)
                           The option --require-label=label:format makes
                           Hadolint check that the label `label` conforms to
                           format requirement `format`
  --strict-labels          Do not permit labels other than specified in
                           `label-schema`
  --disable-ignore-pragma  Disable inline ignore pragmas `# hadolint
                           ignore=DLxxxx`
  -t,--failure-threshold THRESHOLD
                           Exit with failure code only when rules with a
                           severity equal to or above THRESHOLD are violated.
                           Accepted values: [error | warning | info | style |
                           ignore | none] (default: info)

Configure

Configuration files can be used globally or per project. Hadolint looks for configuration files in the following locations or their platform specific equivalents in this order and uses the first one exclusively:

  • $PWD/.hadolint.yaml
  • $XDG_CONFIG_HOME/hadolint.yaml
  • $HOME/.config/hadolint.yaml
  • $HOME/.hadolint/hadolint.yaml or $HOME/hadolint/config.yaml
  • $HOME/.hadolint.yaml

In windows, the %LOCALAPPDATA% environment variable is used instead of XDG_CONFIG_HOME. Config files can have either yaml or yml extensions.

hadolint full yaml config file schema

failure-threshold: string               # name of threshold level (error | warning | info | style | ignore | none)
format: string                          # Output format (tty | json | checkstyle | codeclimate | gitlab_codeclimate | gnu | codacy)
ignored: [string]                       # list of rules
label-schema:                           # See Linting Labels below for specific label-schema details
  author: string                        # Your name
  contact: string                       # email address
  created: timestamp                    # rfc3339 datetime
  version: string                       # semver
  documentation: string                 # url
  git-revision: string                  # hash
  license: string                       # spdx
no-color: boolean                       # true | false
no-fail: boolean                        # true | false
override:
  error: [string]                       # list of rules
  warning: [string]                     # list of rules
  info: [string]                        # list of rules
  style: [string]                       # list of rules
strict-labels: boolean                  # true | false
disable-ignore-pragma: boolean          # true | false
trustedRegistries: string | [string]    # registry or list of registries

hadolint supports specifying the ignored rules using a configuration file. The configuration file should be in yaml format. This is one valid configuration file as an example:

ignored:
  - DL3000
  - SC1010

Additionally, hadolint can warn you when images from untrusted repositories are being used in Dockerfiles, you can append the trustedRegistries keys to the configuration file, as shown below:

ignored:
  - DL3000
  - SC1010

trustedRegistries:
  - docker.io
  - my-company.com:5000
  - "*.gcr.io"

If you want to override the severity of specific rules, you can do that too:

override:
  error:
    - DL3001
    - DL3002
  warning:
    - DL3042
    - DL3033
  info:
    - DL3032
  style:
    - DL3015

failure-threshold Exit with failure code only when rules with a severity above THRESHOLD are violated (Available in v2.6.0+)

failure-threshold: info
override:
  warning:
    - DL3042
    - DL3033
  info:
    - DL3032

Additionally, you can pass a custom configuration file in the command line with the --config option

hadolint --config /path/to/config.yaml Dockerfile

To pass a custom configuration file (using relative or absolute path) to a container, use the following command:

docker run --rm -i -v /your/path/to/hadolint.yaml:/.config/hadolint.yaml hadolint/hadolint < Dockerfile
# OR
docker run --rm -i -v /your/path/to/hadolint.yaml:/.config/hadolint.yaml ghcr.io/hadolint/hadolint < Dockerfile

In addition to config files, Hadolint can be configured with environment variables.

NO_COLOR=1                               # Set or unset. See https://no-color.org
HADOLINT_NOFAIL=1                        # Truthy value e.g. 1, true or yes
HADOLINT_VERBOSE=1                       # Truthy value e.g. 1, true or yes
HADOLINT_FORMAT=json                     # Output format (tty | json | checkstyle | codeclimate | gitlab_codeclimate | gnu | codacy | sarif )
HADOLINT_FAILURE_THRESHOLD=info          # threshold level (error | warning | info | style | ignore | none)
HADOLINT_OVERRIDE_ERROR=DL3010,DL3020    # comma separated list of rule codes
HADOLINT_OVERRIDE_WARNING=DL3010,DL3020  # comma separated list of rule codes
HADOLINT_OVERRIDE_INFO=DL3010,DL3020     # comma separated list of rule codes
HADOLINT_OVERRIDE_STYLE=DL3010,DL3020    # comma separated list of rule codes
HADOLINT_IGNORE=DL3010,DL3020            # comma separated list of rule codes
HADOLINT_STRICT_LABELS=1                 # Truthy value e.g. 1, true or yes
HADOLINT_DISABLE_IGNORE_PRAGMA=1         # Truthy value e.g. 1, true or yes
HADOLINT_TRUSTED_REGISTRIES=docker.io    # comma separated list of registry urls
HADOLINT_REQUIRE_LABELS=maintainer:text  # comma separated list of label schema items

Non-Posix Shells

When using base images with non-posix shells as default (e.g. Windows based images) a special pragma hadolint shell can specify which shell the base image uses, so that Hadolint can automatically ignore all shell-specific rules.

FROM mcr.microsoft.com/windows/servercore:ltsc2022
# hadolint shell=powershell
RUN Get-Process notepad | Stop-Process

Ignoring Rules

Inline ignores

It is also possible to ignore rules by adding a special comment directly above the Dockerfile statement for which you want to make an exception for. Such comments look like # hadolint ignore=DL3001,SC1081. For example:

# hadolint ignore=DL3006
FROM ubuntu

# hadolint ignore=DL3003,SC1035
RUN cd /tmp && echo "hello!"

The comment "inline ignores" applies only to the statement following it.

Global ignores

Rules can also be ignored on a per-file basis using the global ignore pragma. It works just like inline ignores, except that it applies to the whole file instead of just the next line.

# hadolint global ignore=DL3003,DL3006,SC1035
FROM ubuntu

RUN cd /tmp && echo "foo"

Linting Labels

Hadolint is able to check if specific labels are present and conform to a predefined label schema. First, a label schema must be defined either via the command line:

hadolint --require-label author:text --require-label version:semver Dockerfile

or via the config file:

label-schema:
  author: text
  contact: email
  created: rfc3339
  version: semver
  documentation: url
  git-revision: hash
  license: spdx

The value of a label can be either of text, url, semver, hash or rfc3339:

Schema Description
text Anything
rfc3339 A time, formatted according to RFC 3339
semver A semantic version
url A URI as described in RFC 3986
hash Either a short or a long Git hash
spdx An SPDX license identifier
email An email address conforming to RFC 5322

By default, Hadolint ignores any label that is not specified in the label schema. To warn against such additional labels, turn on strict labels, using the command line:

hadolint --strict-labels --require-label version:semver Dockerfile

or the config file:

strict-labels: true

When strict labels is enabled, but no label schema is specified, hadolint will warn if any label is present.

Note on dealing with variables in labels

It is a common pattern to fill the value of a label not statically, but rather dynamically at build time by using a variable:

FROM debian:buster
ARG VERSION="du-jour"
LABEL version="${VERSION}"

To allow this, the label schema must specify text as value for that label:

label-schema:
  version: text

Integrations

To get most of hadolint, it is useful to integrate it as a check in your CI or into your editor, or as a pre-commit hook, to lint your Dockerfile as you write it. See our Integration docs.

Rules

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

  • Rules with the prefix DL are from hadolint. Have a look at Rules.hs to find the implementation of the rules.

  • Rules with the SC prefix are 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 Default Severity Description
DL1001 Ignore Please refrain from using inline ignore pragmas # hadolint ignore=DLxxxx.
DL3000 Error Use absolute WORKDIR.
DL3001 Info 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 Warning Last user should not be root.
DL3003 Warning Use WORKDIR to switch to a directory.
DL3004 Error Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root.
DL3006 Warning Always tag the version of an image explicitly.
DL3007 Warning Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag.
DL3008 Warning Pin versions in apt-get install.
DL3009 Info Delete the apt-get lists after installing something.
DL3010 Info Use ADD for extracting archives into an image.
DL3011 Error Valid UNIX ports range from 0 to 65535.
DL3012 Error Multiple HEALTHCHECK instructions.
DL3013 Warning Pin versions in pip.
DL3014 Warning Use the -y switch.
DL3015 Info Avoid additional packages by specifying --no-install-recommends.
DL3016 Warning Pin versions in npm.
DL3018 Warning Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version>.
DL3019 Info Use the --no-cache switch to avoid the need to use --update and remove /var/cache/apk/* when done installing packages.
DL3020 Error Use COPY instead of ADD for files and folders.
DL3021 Error COPY with more than 2 arguments requires the last argument to end with /
DL3022 Warning COPY --from should reference a previously defined FROM alias
DL3023 Error COPY --from cannot reference its own FROM alias
DL3024 Error FROM aliases (stage names) must be unique
DL3025 Warning Use arguments JSON notation for CMD and ENTRYPOINT arguments
DL3026 Error Use only an allowed registry in the FROM image
DL3027 Warning Do not use apt as it is meant to be an end-user tool, use apt-get or apt-cache instead
DL3028 Warning Pin versions in gem install. Instead of gem install <gem> use gem install <gem>:<version>
DL3029 Warning Do not use --platform flag with FROM.
DL3030 Warning Use the -y switch to avoid manual input yum install -y <package>
DL3032 Warning yum clean all missing after yum command.
DL3033 Warning Specify version with yum install -y <package>-<version>
DL3034 Warning Non-interactive switch missing from zypper command: zypper install -y
DL3035 Warning Do not use zypper dist-upgrade.
DL3036 Warning zypper clean missing after zypper use.
DL3037 Warning Specify version with zypper install -y <package>[=]<version>.
DL3038 Warning Use the -y switch to avoid manual input dnf install -y <package>
DL3040 Warning dnf clean all missing after dnf command.
DL3041 Warning Specify version with dnf install -y <package>-<version>
DL3042 Warning Avoid cache directory with pip install --no-cache-dir <package>.
DL3043 Error ONBUILD, FROM or MAINTAINER triggered from within ONBUILD instruction.
DL3044 Error Do not refer to an environment variable within the same ENV statement where it is defined.
DL3045 Warning COPY to a relative destination without WORKDIR set.
DL3046 Warning useradd without flag -l and high UID will result in excessively large Image.
DL3047 Info wget without flag --progress will result in excessively bloated build logs when downloading larger files.
DL3048 Style Invalid Label Key
DL3049 Info Label <label> is missing.
DL3050 Info Superfluous label(s) present.
DL3051 Warning Label <label> is empty.
DL3052 Warning Label <label> is not a valid URL.
DL3053 Warning Label <label> is not a valid time format - must conform to RFC3339.
DL3054 Warning Label <label> is not a valid SPDX license identifier.
DL3055 Warning Label <label> is not a valid git hash.
DL3056 Warning Label <label> does not conform to semantic versioning.
DL3057 Ignore HEALTHCHECK instruction missing.
DL3058 Warning Label <label> is not a valid email format - must conform to RFC5322.
DL3059 Info Multiple consecutive RUN instructions. Consider consolidation.
DL3060 Info yarn cache clean missing after yarn install was run.
DL3061 Error Invalid instruction order. Dockerfile must begin with FROM, ARG or comment.
DL4000 Error MAINTAINER is deprecated.
DL4001 Warning Either use Wget or Curl but not both.
DL4003 Warning Multiple CMD instructions found.
DL4004 Error Multiple ENTRYPOINT instructions found.
DL4005 Warning Use SHELL to change the default shell.
DL4006 Warning Set the SHELL option -o pipefail before RUN with a pipe in it
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 [ .. ] or [[ .. ]].
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 | .. or cmd file | .. instead.
SC2015 Note that A && B || C is not if-then-else. C may run when A is true.
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.
SC2039 In POSIX sh, something is undefined.
SC2046 Quote this to prevent word splitting
SC2086 Double quote to prevent globbing and word splitting.
SC2140 Word is in the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?
SC2154 var is referenced but not assigned.
SC2155 Declare and assign separately to avoid masking return values.
SC2164 Use cd ... || exit in case cd fails.

Develop

If you are an experienced Haskeller, we would be very grateful if you would tear our code apart in a review.

To compile, you will need a recent Haskell environment and cabal-install.

Setup

  1. Clone repository

    git clone --recursive [email protected]:hadolint/hadolint.git
  2. Install dependencies and compile source

    cabal configure
    cabal build
  3. (Optional) Install Hadolint on your system

    cabal install

REPL

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

# start the repl
cabal repl
# overload strings to be able to use Text
:set -XOverloadedStrings
# import parser library
import Language.Docker
# parse instruction and look at AST representation
parseText "FROM debian:jessie"

Tests

Compile with unit tests and run them:

cabal configure --enable-tests
cabal build --enable-tests
cabal test

Run integration tests:

./integration_test.sh

AST

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

Building against custom libraries

Hadolint uses many libraries to do the dirty work. In particular, language-docker is used to parse Dockerfiles and produce an AST which then can be analyzed. To build Hadolint against a custom version of such libraries, do the following. This example uses language-docker, but it would work with any other library as well.

  1. In the same directory (e.g. /home/user/repos) clone Hadolint and language-docker git repositories
cd /home/user/repos
git clone https://github.com/hadolint/hadolint.git
git clone https://github.com/hadolint/language-docker.git
  1. Make your modifications to language-docker

  2. In the Hadolint repo, edit the cabal.project file, such that the packages property points to the other repo too

[...]
packages:
  .
  ../language-docker
[...]
  1. Recompile Hadolint and run the tests
cd /home/user/repos/hadolint
cabal configure --enable-tests
cabal build --enable-tests
cabal test

Alternatives

hadolint's People

Contributors

alajmo avatar backus avatar bephinix avatar cpennington avatar dariodsa avatar dependabot[bot] avatar djh82 avatar jllopes avatar kiddo3 avatar kornicameister avatar lorenzo avatar lukasmartinelli avatar lunkentuss avatar m-ildefons avatar markus1189 avatar mathbunnyru avatar michaellzc avatar mikezraly avatar nvuillam avatar pbullian avatar sasanidas avatar sdwolfz avatar seaerchin avatar stephengroat avatar swarnimarun avatar tanmay-pnaik avatar wesley-dean-flexion avatar wfleming avatar wwuck avatar zemanlx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hadolint's Issues

ENV parameters separated by multiple spaces -> unexpected " " expecting "\""

I have found a strange use case - with formatted ENV parameters:

hadolint problem

$   docker run --rm -i lukasmartinelli/hadolint < Dockerfile1
"/dev/stdin" (line 2, column 12):
unexpected " "
expecting "\""

Dockerfile

$   cat Dockerfile1
FROM debian:jessie
ENV PGUSER             osm
ENV PGPASSWORD         osm
ENV LANG               en_US.utf8

Docker build - OK

$   docker build -t t1 -f Dockerfile1 .
Sending build context to Docker daemon 4.096 kB
Step 1 : FROM debian:jessie
 ---> 1b01529cc499
Step 2 : ENV PGUSER osm
 ---> Using cache
 ---> b21b356a3fa2
Step 3 : ENV PGPASSWORD osm
 ---> Using cache
 ---> 606c805a31af
Step 4 : ENV LANG en_US.utf8
 ---> Using cache
 ---> dd1174a8770c
Successfully built dd1174a8770c

Hadolint image hash id

$ docker images lukasmartinelli/hadolint:latest
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
lukasmartinelli/hadolint   latest              5c7f5c36ce6f        2 hours ago         1.522 GB

Support `HEALTHCHECK` instruction

Docker 1.12 adds a HEALTHCHECK instruction which hadolint should probably at least support and ignore. Currently this instruction will cause a parse error

Dockerfile must be explicitly specified as '-' to use the stdin mode

From the start hadolint has been having the same behavior where Dockerfile must be specified. And since v1.1 we introduced - as an alias of stdin, piping a Dockerfile to stdin for hadolint to process will have to specify a dash - to make it recognize input coming from stdin.

In order to make it more feel like a native unix utility, can we default the input file to be stdin when Dockerfile param is omitted? I know it may seem a bit nitpicking but it will bring hadolint more in line with other most frequently used unix utilities like cat, grep, gzip and my new favorite jq etc. Additionally, perhaps we shouldn't display /dev/stdin is the input file is stdin? I am open for discussion.

$ cat linters/hadolint/Dockerfile | /usr/local/Cellar/hadolint/1.0/bin/hadolint -i
L5 DL3020 Use COPY instead of ADD for files and folders
$ cat linters/hadolint/Dockerfile | /usr/local/Cellar/hadolint/1.2.1/bin/hadolint -
/dev/stdin:5 DL3020 Use COPY instead of ADD for files and folders
$ # it would nice to be able to simplify as `cat Dockerfile | hadolint`

I noticed that the CMD in Dockerfile is CMD ["hadolint", "-"] so it's less of an issue for dockerized hadolint per se. (PS: perhaps we should use ENTRYPOINT instead of CMD?)

Parsing fails when there is a newline in ENV

FROM debian:stable
MAINTAINER Foo <foo@foo>

ENV TEST=1 \
  TEST2=2

Fails with:

"<string>" (line 4, column 13):
unexpected "\n"
FROM debian:stable
MAINTAINER Foo <foo@foo>

ENV TEST=1 TEST2=2

Works.

Linter output inconsistent between homebrew built command vs. Docker container command

For some reason, the docker container's hadolint linter prints a <string> in the place of L as in line, as you can see below:

$ brew info hadolint
hadolint: stable 1.0 (bottled)
Smarter Dockerfile linter to validate best practices.
http://hadolint.lukasmartinelli.ch/
/usr/local/Cellar/hadolint/1.0 (5 files, 18M) *
  Poured from bottle on 2016-04-06 at 16:24:53
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/hadolint.rb
==> Dependencies
Build: ghc ✔, cabal-install ✔

$ docker images
REPOSITORY                                      TAG                                        IMAGE ID            CREATED             SIZE
lukasmartinelli/hadolint                        latest                                     da0685f918c0        3 weeks ago         1.286 GB
$ docker run --rm -it --entrypoint bash lukasmartinelli/hadolint:latest 
root@28bda4443361:/opt/hadolint# hadolint -v
Haskell Dockerfile Linter v1.0
root@28bda4443361:/opt/hadolint# exit

$ hadolint Dockerfile
L4 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
L4 DL3009 Delete the apt-get lists after installing something
L4 DL3015 Avoid additional packages by specifying `--no-install-recommends`
L6 DL3020 Use COPY instead of ADD for files and folders
L13 DL3020 Use COPY instead of ADD for files and folders

$ docker run --rm -i lukasmartinelli/hadolint < Dockerfile 
<string>:4 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
<string>:4 DL3009 Delete the apt-get lists after installing something
<string>:4 DL3015 Avoid additional packages by specifying `--no-install-recommends`
<string>:6 DL3020 Use COPY instead of ADD for files and folders
<string>:13 DL3020 Use COPY instead of ADD for files and folders

Parsing fails when there are multiple quoted ENV vars

FROM debian:stable
MAINTAINER Foo <foo@foo>

ENV foo="Hello World" bar="Hello World Again"

Fails with:

"<string>" (line 4, column 23):
unexpected 'b'
expecting space, "\t", "ONBUILD", "FROM", "COPY", "RUN", "WORKDIR", "ENTRYPOINT", "VOLUME", "EXPOSE", "ENV", "ARG", "USER", "LABEL", "STOPSIGNAL", "CMD", "MAINTAINER", "ADD", "#" or end of input

While,

FROM debian:stable
MAINTAINER Foo <foo@foo>

ENV foo=HelloWorld bar=HelloWorldAgain

Works.

Could be related to #7 , I didn't know if I should open another issue or add to that one.

Error code 0 not actually returned upon clean lint pass

Thank you for sharing Hadolint, it is quite wonderful and gave me good suggestions on my Dockerfiles.

In the hopes of improving it's quality I'd like to report a small bug when linting a Dockerfile that passes with no problems where Hadolint returns a failure error code instead of a successful one, although from the error it appears it is trying to return with a successful one.

So I am able to successfully run hadolint in a gitlab ci runner. In the standard problems-exist case, when there are errors, it provides output like:

HEAD is now at b8f0489... Added gitlab-ci.

$ hadolint ./Dockerfile
L4 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
L4 DL3009 Delete the apt-get lists after installing something
L4 DL3015 Avoid additional packages by specifying `--no-install-recommends`
L18 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
L18 DL3015 Avoid additional packages by specifying `--no-install-recommends`
L20 DL3003 Use WORKDIR to switch to a directory

and returns a non-zero exit code.

However, once I fix those issues I get:

HEAD is now at 244996c... Correcting problems from Dockerfile linter hadolint

$ hadolint ./Dockerfile
hadolint: exitWith: invalid argument (ExitFailure 0)

And then it still returns with a non-zero exit code. :(


Notably I am unable to successfully run hadolint via a docker container or through it inside its container. I get

hadolint ./Dockerfile
"./Dockerfile" (line 5, column 5):
unexpected "&"
expecting space, "\t", "ONBUILD", "FROM", "COPY", "RUN", "WORKDIR", "ENTRYPOINT", "VOLUME", "EXPOSE", "ENV", "ARG", "USER", "LABEL", "STOPSIGNAL", "CMD", "MAINTAINER", "ADD" or "#"
exit

I am not reporting this latter part as a problem, but just wanting to note that while the error code issue is real, it may be part of a larger problem as it can obviously parse the Dockerfile but seems machine or environment (like interactive non-teletype shell?) dependent in doing so.

`unexpected "\n"` at ARG instruction

Hi! Just wanted to report a potential bug with the parser. When I run:

docker run --rm -v $(pwd):/lint lukasmartinelli/hadolint hadolint /lint/Dockerfile

...from a directory containing this Dockerfile:

FROM jtrim/heroku-phoenix

ARG MIX_ENV
ENV MIX_ENV ${MIX_ENV:-prod}

ADD mix.exs /app/user/
ADD mix.lock /app/user/
RUN mix deps.get

ADD package.json /app/user/
RUN npm install

ADD ./web/static /app/user/web/static/
ADD ./brunch-config.js /app/user/
RUN ./node_modules/brunch/bin/brunch build --production
RUN mix phoenix.digest

ADD . .

RUN mix compile

EXPOSE 4000 4001
CMD ["mix", "phoenix.server"]

...I get the output:

$ docker run --rm -v $(pwd):/lint lukasmartinelli/hadolint hadolint /lint/Dockerfile
"/lint/Dockerfile" (line 3, column 12):
unexpected "\n"

When I comment out the the ARG line:

FROM jtrim/heroku-phoenix

#ARG MIX_ENV
ENV MIX_ENV ${MIX_ENV:-prod}

ADD mix.exs /app/user/
ADD mix.lock /app/user/
RUN mix deps.get

ADD package.json /app/user/
RUN npm install

ADD ./web/static /app/user/web/static/
ADD ./brunch-config.js /app/user/
RUN ./node_modules/brunch/bin/brunch build --production
RUN mix phoenix.digest

ADD . .

RUN mix compile

EXPOSE 4000 4001
CMD ["mix", "phoenix.server"]

...everything seems to run just fine:

$ docker run --rm -v $(pwd):/lint lukasmartinelli/hadolint hadolint /lint/Dockerfile
L6 DL3020 Use COPY instead of ADD for files and folders
L7 DL3020 Use COPY instead of ADD for files and folders
L10 DL3020 Use COPY instead of ADD for files and folders
L13 DL3020 Use COPY instead of ADD for files and folders
L14 DL3020 Use COPY instead of ADD for files and folders
L18 DL3020 Use COPY instead of ADD for files and folders
L1 DL3006 Always tag the version of an image explicitely.
DL4000 Specify a maintainer of the Dockerfile

Let me know if I can provide any more information. Thanks for writing this tool!

Provide Static Binaries

I now provide binaries on the v1.1 build.

https://github.com/lukasmartinelli/hadolint/releases/tag/v1.1

These are not entirely statically linked (oh Haskell why doesn't it work).
The binaries are built by the CI infrastructure (you can also inspect the builds and download it from there) and perhaps work on your machines?

I hope this makes it easier to install since as of now hadolint biggest problem is the pain to install it.

Anyone want to try the Windows version?
@ye could you check whether the v1.1 works for you?

Parsing fails when there is a newline in CMD

This is very similar to #7, but I thought I'd make a separate issue for the sake of completeness.

Given this Dockerfile:

FROM busybox

CMD true \
 && true

parsing fails with:

"<string>" (line 4, column 2):
unexpected "&"
expecting space, "\t", "ONBUILD", "FROM", "COPY", "RUN", "WORKDIR", "ENTRYPOINT", "VOLUME", "EXPOSE", "ENV", "ARG", "USER", "LABEL", "STOPSIGNAL", "CMD", "MAINTAINER", "ADD" or "#"

Parsing fails when line in multiline block ends with extraneous space

Problem

Running hadolint given a Dockerfile with a multiline block with an additional space at the end of the line continuation symbol.

RUN yum install -y \
    git \
    imagemagick \ 
    java-1.8.0-openjdk \
    java-1.8.0-openjdk-devel \
    mysql

_Note the added SPACE after imagemagick that is the cause of the problem_

Current Behavior:

"/src/Dockerfile" (line 21, column 2):
unexpected "&"
expecting space, "\t", "ONBUILD", "FROM", "COPY", "RUN", "WORKDIR", "ENTRYPOINT", "VOLUME", "EXPOSE", "ENV", "ARG", "USER", "LABEL", "STOPSIGNAL", "CMD", "MAINTAINER", "ADD" or "#"

Fixed by

Removing the extra space.

DL3020 fires on ADD ./files/my.tar.gz

Maybe a list of known archive extensions could be whitelisted.

Love the tool by the way. Only raising this cos the page asks for issues to be raised.

Parsing fails when environment variables are set as "ENV <key> <value>"

According to the docs there are two ways to use ENV, however:

This works: ENV <key>=<value>

This does not and will cause the parser to fail: ENV <key> <value>

Object {error: ""<string>" (line 5, column 14):↵unexpected "\n""}
editor.js:17 Uncaught TypeError: Cannot read property 'message' of undefined

Using the example from a recent pull request:

FROM debian:stable
MAINTAINER Foo <foo@foo>
ENV foo Hello World

Other things such as putting random letters in Dockerfiles will also trigger a Javascript error, again line 17 from editor.js is referenced.

FROM debian:stable
MAINTAINER Foo <foo@foo>
ENV foo="Hello World"
asdf

DL3009 Not recognizing clean up in multiple RUN statements

I ran the linter on my Dockerfile and received the DL3009 warning. After looking at the docs I found that my Dockerfile has all of the steps needed to satisfy the rule however, they occur in different RUN statements. It would be great if the linter recognized this.

RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get install --no-install-recommends -y \
build-essential \
git \
&& apt-get clean
.
.
.
RUN rm -rf /var/lib/apt/lists/* \
/tmp/* \
/var/tmp/*

DL3013 rule pip version pinning does not catch `pip3` lines

This is the current v1.0 version (still supports stdin). As you can see, the pip3 line does not trigger DL3013 violation check.

$ hadolint -v
Haskell Dockerfile Linter v1.0

$ hadolint -i <<EOF
> FROM python:3.4
> MAINTAINER Ye Wang <[email protected]>
> 
> RUN pip3 install requests sqlalchemy
> EOF

As long as I changed the pip3 to pip, hadolint will pick up that line and reports an violation.

$ hadolint -i <<EOF
> FROM python:3.4
> MAINTAINER Ye Wang <[email protected]>
> 
> RUN pip install requests sqlalchemy
> EOF
L4 DL3013 Pin versions in pip. Instead of `pip install <package>` use `pip install <package>==<version>`

DL3008 false positive

Hi Lukas,

Dockerfile:

FROM ubuntu:15.10
MAINTAINER Oleksii Dzhulai [email protected]

WORKDIR /tmp

RUN sed -i 's/http://archive.ubuntu.com/ubuntu//mirror://mirrors.ubuntu.com/mirrors.txt/' /etc/apt/sources.list
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install --no-install-recommends -y
python-demjson=2.2.2*
wget=1.16.1*
git=1:2.5.0*
ruby=1:2.1.*
&& apt-get clean
&& rm -rf /var/lib/apt/lists/*
RUN gem install ruby-lint

Output:
/bin/sh -c docker\ run\ --rm\ -i\ lukasmartinelli/hadolint\ <\ buildcont/Dockerfile
L7 DL3008 Pin versions in apt get install. Instead of apt-get install <package> use apt-get install <package>=<version>

Please advise

Thanks

hlint

As it happens I also started to look into Haskell and a colleague of mine just told me about hlint. Interestingly enough I never saw that mentioned in any of the usual books. It looks like an absolutely amazing learning tool, so I just wanted to let you know about it too :) Also, it seems so obvious to use a linter to improve a linter.

$ hlint src/*.hs
src/Main.hs:30:16: Error: Redundant do
Found:
  do parseFile file >>= checkAst
Why not:
  parseFile file >>= checkAst

src/Parser.hs:78:25: Warning: Use string literal
Found:
  [' ', '=', '\n']
Why not:
  " =\n"

src/Parser.hs:154:18: Warning: Redundant bracket
Found:
  (init line) ++ newLine
Why not:
  init line ++ newLine

src/Rules.hs:44:1: Error: Eta reduce
Found:
  instructionRule code severity message f
    = mapInstructions (Metadata code severity message) f
Why not:
  instructionRule code severity message
    = mapInstructions (Metadata code severity message)

src/Rules.hs:84:1: Error: Eta reduce
Found:
  shellcheckBash dockerfile = concatMap check dockerfile
Why not:
  shellcheckBash = concatMap check

src/Rules.hs:107:11: Error: Eta reduce
Found:
  check instructions = any maintainer instructions
Why not:
  check = any maintainer

src/Rules.hs:203:38: Error: Redundant bracket
Found:
  (noOption)
Why not:
  noOption

src/Rules.hs:260:35: Error: Use elem
Found:
  any (True ==)
Why not:
  elem True

8 suggestions

Better usage page and better validating cli parameters.

Currently the usage page prints out a single line and does nothing to explain what each flag does.

I accidentally use -v and thought it was a verbose flag but it turns out to be print version flag and then I am getting an error see below.

$ hadolint -h
Usage: hadolint [-vhi] <file>

$ hadolint -v /Users/ye/test/Dockerfile
hadolint: src/Main.hs:(16,1)-(31,63): Non-exhaustive patterns in function parse

DL3000 not honouring the exception part

I have Dockerfile which has following syntax

  ENV work /raven
  WORKDIR ${work}

when i tried online version to check, it is throwing DL3000 error. Ideally as per document this comes as part of exception because work is already set in ENV.

Add new rule for `npm install`

If a single-line or multi-line RUN with npm install does not have –silent –progress=false flags then suggest adding them.

as it’s known to improve NPM installation time 2x-3x. npm/npm#11283

This is the source of this tip.

Allow to pass a set of rules to ignore

It would be nice if it would be possible to pass a set of rules that should be ignored by the linter. Compare to python, where pep8 is an accepted standard, but almost every project defines 2-3 well-thought-of exceptions. I cannot simply ignore the output, as I want to use hadolint in a CI system.

FWIW, the rule that I disagree with is DL3008.

Hadolint fails multiline commands

Running hadolint Dockerfile against the following sample,

FROM debian:jessie

# Environment variables
ENV NODE_VERSION=v5.7.1 \
    DEBIAN_FRONTEND=noninteractive

gives the following error.

Dockerfile" (line 4, column 26):
unexpected "D"
expecting space, "\t", "ONBUILD", "FROM", "COPY", "RUN", "WORKDIR", "ENTRYPOINT", "VOLUME", "EXPOSE", "ENV", "ARG", "USER", "LABEL", "STOPSIGNAL", "CMD", "MAINTAINER", "ADD" or "#"

Is it getting hung up on the \ character or is there something else happening?

lint missing arguments

ADD needs two arugmemts and .
The Dockerfile validation doesn't hint it if we miss arguments.

FROM alpine:latest
COPY a.txt

Also other Dockerfile Instructions.

rules based on Docker Official Images Guideline

maybe it is worth to check and create a some new rules
based on Docker Official Images Guideline: https://github.com/docker-library/official-images#review-guidelines

some example, as I know no check exists yet :

Security rule suggestion:

IF [ curl / wget] command used

  • via "http://" url
  • and no "Sha1sum / Sha256sum / Sha512sum/ Md5sum"

THEN security warning ~ ~"download the file over http with no verification."

see : https://github.com/docker-library/official-images/blob/master/README.md#security

The Dockerfile should be written to help mitigate man-in-the-middle attacks during build: using https where possible; importing PGP keys with the full fingerprint in the Dockerfile to check package signing; embedding checksums directly in the Dockerfile if PGP signing is not provided. 
Cacheability rule - avoid "COPY . "

see : https://github.com/docker-library/official-images/blob/master/README.md#cacheability

Avoid COPY/ADD whenever possible, but when necessary, be as specific as possible (ie, COPY one-file.sh /somewhere/ instead of COPY . /somewhere).
The reason for this is that the cache for COPY instructions considers file mtime changes to be a cache bust, which can make the cache behavior of COPY unpredictable sometimes, especially when .git is part of what needs to be COPYed (for example).

two forms of output

1 .
Dockerfile:

CMD

Hint:

 /tmp/Dockerfile932271731 DL4000 Specify a maintainer of the Dockerfile

2 .
Dockerfile:

CMDs

Hint:

"/tmp/Dockerfile634358372" (line 1, column 5):
unexpected 'S'
expecting end of "CMD"

Those two forms of output are difficult for us to parse and analyse.

Can we unify output in one form?

Parse fails with multiline comments

I have a multiline comment like this in my Dockerfile:

# line 1
# line 2
RUN apt-get update ...

which results in anunexpected "\n" error.

Exposed udp ports don't work

When I have a Dockerfile that is exposing udp ports using the EXPOSE 53/udp syntax I get the following error.
"<string>" (line 18, column 12): unexpected '/' expecting space, "\t", "ONBUILD", "FROM", "COPY", "RUN", "WORKDIR", "ENTRYPOINT", "VOLUME", "EXPOSE", "ENV", "ARG", "USER", "LABEL", "STOPSIGNAL", "CMD", "MAINTAINER", "ADD", "#" or end of input

pip version pin false positive

I think I'm running into a false positive. I found the original issue and PR that fixed this, but I think there's one more corner case.

Here's my Dockerfile:

FROM ubuntu:14.04
MAINTAINER Infrastructure <[email protected]>

ENV GCC_VERSION=4:4.8.2-1ubuntu6
ENV PYTHON_DEV_VERSION=2.7.5-5ubuntu3
ENV PYTHON_PIP_VERSION=1.5.4-1ubuntu4

# also, depends on https://github.com/graphite-project/carbon/pull/486
# once 0.9.16 is released, no need for special carbon install belos
ENV GRAPHITE_VERSION 0.9.15

RUN apt-get update && \
    apt-get install -y --no-install-recommends "gcc=$GCC_VERSION" "python-dev=$PYTHON_DEV_VERSION" "python-pip=$PYTHON_PIP_VERSION" && \
    pip install whisper=="$GRAPHITE_VERSION" && \
    pip install graphite-web=="$GRAPHITE_VERSION" && \
    pip install https://github.com/Banno/carbon/tarball/0.9.x-fix-events-callback && \
    apt-get autoremove -y gcc && \
    rm -rf /var/lib/apt/lists/*

I've tried running with quotes around the package and version e.g. "whisper==$GRAPHITE_VERSION" and removing the hyphen e.g. graphiteweb=="$GRAPHITE_VERSION", but I'm still getting an error.

/dev/stdin:12 DL3013 Pin versions in pip. Instead of `pip install <package>` use `pip install <package>==<version>`

I'm running the latest container pulled yesterday from the docker hub.

adam@Planet-X -- ~:  docker images | grep hadolint
lukasmartinelli/hadolint                    latest               5ae761b44162        2 weeks ago         1.527 GB

Colon triggers DL4000 Specify a maintainer of the Dockerfile

Fails:

FROM scratch
MAINTAINER John Doe <[email protected]>

# See: The colon generates DL4000
ENTRYPOINT ["/opt/hugo/bin/hugo"]

This works:

FROM scratch:1.0
MAINTAINER John Doe <[email protected]>

# See: It works now
ENTRYPOINT ["/opt/hugo/bin/hugo"]

But tags on "scratch" do not make sense. Removing all colons you get this:

FROM scratch
MAINTAINER John Doe <[email protected]>

# See You get DL3006 Always tag the version of an image explicitly.
ENTRYPOINT ["/opt/hugo/bin/hugo"]

For this particular case, you should allow not specifying tags.

Parsing fails when file ends with no newline

Running hadolint given a Dockerfile with no newline at the end of its last line.

Actual Behavior:

$ hadolint ./Dockerfile
"./Dockerfile" (line 36, column 99):
unexpected end of input
expecting "\n" or "\r"

Fixed by

Adding a new line to the end of the Dockerfile.

Question re: DL3003 Use WORKDIR to switch to a directory.

I have a Dockerfile where I need to manually install some software using a command like this:

# Install Python 2.7
WORKDIR /tmp/
RUN wget --no-check-certificate "https://www.python.org/ftp/python/2.7.6/Python-2.7.6.tar.xz" \
 && tar xf Python-2.7.6.tar.xz \
 && cd Python-2.7.6 \
 && ./configure --prefix=/usr/local \
 && make && make altinstall \
 && cd .. \
 && rm -rf Python-2.7.6 \
 && rm -rf Python-2.7.6.tar.xz

Is the suggestion really to turn that into

# Install Python 2.7
WORKDIR /tmp/
RUN wget --no-check-certificate "https://www.python.org/ftp/python/2.7.6/Python-2.7.6.tar.xz" \
 && tar xf Python-2.7.6.tar.xz
WORKDIR /tmp/python2.7-installation/Python-2.7.6
RUN ./configure --prefix=/usr/local \
 && make && make altinstall
WORKDIR /tmp/python2.7-installation/
RUN rm -rf Python-2.7.6 \
 && rm -rf Python-2.7.6.tar.xz

While arguably more readable, that more than doubles the amount of layers created. So I just wanted to see what your thoughts were on commands like these. :)

DL3020 Use ADD to add URL resources (fetch from network instead of local copy)

While majority of the cases COPY is preferred over ADD in Docker's best practices guide, there are two exceptions [1]:

  1. tarball file (either local or remote) extraction into image during build
  2. resources fetched from the network

Currently, DL3020 rule doesn't account for the case 2 where is an URLs, which should not be a violation of the best practices. See below for a legitimate use case:

$ cat <<EOF | hadolint --ignore DL3007 -
> FROM busybox:latest
> MAINTAINER Ye Wang <[email protected]>
> 
> ADD "https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64" /bin/jq
> RUN chmod +x /bin/jq
> ENTRYPOINT ["jq"]
> EOF
/dev/stdin:4 DL3020 Use COPY instead of ADD for files and folders

Note: although they said "fetching packages from remote URLs is strongly discouraged" [2] in the best practices guide, I think that is debatable as the argument is against remote URLs to tarball archives that leads to inefficient file extractions. So that's really first case above.

[1] https://docs.docker.com/engine/reference/builder/#add
[2] https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/add-or-copy

Display Dockerfile path when showing the results

Currently, only line numbers are printed out when there is a rule violation detected. It would be great to have the path to the Dockerfile examined printed out as well, just like most of other linters/compilers do.
That way, we can write a simple bash script to lint a whole bunch of Dockerfiles and the results would make more sense as to while violation is pertaining to which Dockerfile.
Additionally, hadolint perhaps can take a directory and find the Dockerfiles in it and show results altogether instead of having to run it one Dockerfile at a time.

Homebrew installation, release?

It would be nice to be able to install this from homebrew (like shellcheck), without having to install cabal.

Typically homebrew formulas are pinned to releases/versions, will you be creating a release?

EXPOSE with variables doesn't work

The Dockerfile validation fails when the ports arguments to the EXPOSE command are declared as environment variables. Take this dummy, useless code as example:

FROM alpine:latest
ENV PORT=8080
EXPOSE $PORT

This is the output:

$ docker run --rm -i lukasmartinelli/hadolint < Dockerfile
"/dev/stdin" (line 3, column 8):
unexpected '$'
expecting space, "\t", "ONBUILD", "FROM", "COPY", "RUN", "WORKDIR", "ENTRYPOINT", "VOLUME", "EXPOSE", "ENV", "ARG", "USER", "LABEL", "STOPSIGNAL", "CMD", "MAINTAINER", "ADD", "#" or end of input

But the image inspection show that the correct port was exposed:

$ docker build --tag=sample -f Dockerfile .
$ docker inspect sample | grep -A2 Exposed
            "ExposedPorts": {
                "8080/tcp": {}
            },

Declaring variables with the exposed ports allow for reuse inside the Dockerfile.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.