Code Monkey home page Code Monkey logo

language-docker'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

language-docker's People

Contributors

arguggi avatar cpennington avatar dtan4 avatar ggreif avatar lorenzo avatar lukasmartinelli avatar m-ildefons avatar matsubara0507 avatar moshen avatar pdehaan avatar pdericson avatar ryanglscott avatar wfleming avatar wyrihaximus avatar yamadapc 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

Watchers

 avatar  avatar  avatar  avatar  avatar

language-docker's Issues

'label' has to quote arguments

λ> putDockerfileStr $ label [("maintainer","Peter Simons <[email protected]>")]
LABEL maintainer=Peter Simons <[email protected]>

gives a Dockerfile that's syntactically incorrect. I would expect label to quote that value as needed. Is that not how it's supposed to be used? Do I need to quote these values myself?

Stage name of image is part of name/tag/sha

In a Dockerfile used for multistage build, a stage of image is part of name/tag/sha

> parseString "FROM ubuntu AS ubuntu-build"
Right [InstructionPos (From (UntaggedImage "ubuntu AS ubuntu-build")) "<string>" 1]

> parseString "FROM ubuntu:16.04 as ubuntu-build"
Right [InstructionPos (From (TaggedImage "ubuntu" "16.04 as ubuntu-build")) "<string>" 1]

> parseString "FROM ubuntu@sha256:0ef2e08ed3fabfc44002ccb846c4f2416a2135affc3ce39538834059606f32dd as ubuntu-build"
Right [InstructionPos (From (TaggedImage "ubuntu@sha256" "0ef2e08ed3fabfc44002ccb846c4f2416a2135affc3ce39538834059606f32dd as ubuntu-build")) "<string>" 1]

It would be nice to get a stage name also from parser.

https://docs.docker.com/engine/userguide/eng-image/multistage-build/#name-your-build-stages

Wrong escaping of `\` when parsing ENV instructions

When parsing a Dockerfile with an ENV instruction containing a \ in the value of an environment variable, this is not escaped correctly:

ENV DOCKER_PASSWORD="aaa\aaa"

is parsed to

[InstructionPos {instruction = Env [("DOCKER_PASSWORD","aaa\\aaa")], sourcename = "./test.dockerfile", lineNumber = 1}]

This shows two \\ in the parsed value while I would expect there to be none as the \ is here used to (uselessly) escape the a.

compilation with ghc 8.8.2 stack 15.1

I am trying to get our hadolint/hadolint working with the latest LTS stack. Unfortunately, build is failing for this repo with the latest stack 15.1 which is using ghc 8.8.2 and I am getting this error:

/home/zemanlx/git/language-docker/src/Language/Docker/Syntax/Lift.hs:18:10: error:
    Duplicate instance declarations:
      instance Lift Text.Text
        -- Defined at src/Language/Docker/Syntax/Lift.hs:18:10
      instance Lift Text.Text -- Defined in ‘Data.Text’
   |
18 | instance Lift Text.Text where
   |          ^^^^^^^^^^^^^^

I bet it is something that can be solved somehow trivially but I do not know how. If I can get hint @lorenzo I am happy to make PR for it.

Return EXPOSE ports as tuple

Current state

> parseString "EXPOSE 80/tcp 53/udp"
Right [InstructionPos (Expose (PortStr "80/tcp 53/udp")) "<string>" 1]
> parseString "EXPOSE 80 53/udp"
Right [InstructionPos (Expose (PortStr "80 53/udp")) "<string>" 1]
> parseString "EXPOSE 80 53"
Right [InstructionPos (Expose (Ports [80,53])) "<string>" 1]

This means that I have to parse it again when I get string to get ports and protocols. I would like to get it beck from parser as pairs portnumber/protocol where default protocol is tcp.

https://docs.docker.com/engine/reference/builder/#expose

Proposed state

> parseString "EXPOSE 80/tcp 53/udp"
Right [InstructionPos (Expose (Ports [(80,"tcp"), (53,"udp")])) "<string>" 1]
> parseString "EXPOSE 80 53/udp"
Right [InstructionPos (Expose (Ports [(80,"tcp"), (53,"udp")])) "<string>" 1]
> parseString "EXPOSE 80 53"
Right [InstructionPos (Expose (Ports [(80,"tcp"), (53,"tcp")])) "<string>" 1]

Unexpected parser error in language-docker-6.0.1

I tried to parse https://github.com/peti/opensuse-docker/blob/master/tw-haskell-dev/Dockerfile and got the following error:

../opensuse-docker/tw-haskell-dev/Dockerfile:18:2:
unexpected '&'
expecting '#', ADD, ARG, CMD, COPY, ENTRYPOINT, ENV, EXPOSE, FROM, HEALTHCHECK, LABEL, MAINTAINER, ONBUILD, RUN, SHELL, STOPSIGNAL, USER, VOLUME, WORKDIR, end of input, or the rest of a new line followed by the next instruction

Curiously enough, docker parses and handles that Dockerfile just fine, so I reckon this is more likely to be an issue in the parser than an actual error in the file?

Test files missing from release tarball

Tests can't be executed from the hackage sdist due to missing the following files:

  • test/fixtures/Dockerfile.bom.utf32be
  • test/fixtures/Dockerfile.bom.utf32le
  • test/fixtures/Dockerfile.bom.utf16be
  • test/fixtures/Dockerfile.bom.utf16le
  • test/fixtures/Dockerfile.bom.utf8

Cannot parse image name with "registry:port"

Raised in hadolint/hadolint#169

We cannot parse

FROM myregistry.com:5000/imagename:tag

because we expect only one colon :, but having a port number in registry name is usual for local/private registries.


Extended description

An image name is made up of slash-separated name components, optionally prefixed by a registry hostname. The hostname must comply with standard DNS rules, but may not contain underscores. If a hostname is present, it may optionally be followed by a port number in the format :8080. If not present, the command uses Docker’s public registry located at registry-1.docker.io by default. Name components may contain lowercase letters, digits and separators. A separator is defined as a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator.

A tag name must be valid ASCII and may contain lowercase and uppercase letters, digits, underscores, periods and dashes. A tag name may not start with a period or a dash and may contain a maximum of 128 characters.

https://docs.docker.com/engine/reference/commandline/tag/#extended-description

src/Language/Docker/PrettyPrint.hs:91:25: error: Not in scope: 'fail'

Trying to build 4.0.1 here with ghc 8.0.1, and seeing this issue:

./setup build
Building language-docker-4.0.1...
Preprocessing library language-docker-4.0.1...
[ 1 of 11] Compiling Paths_language_docker ( dist/build/autogen/Paths_language_docker.hs, dist/build/Paths_language_docker.o )
[ 2 of 11] Compiling Language.Docker.Syntax ( src/Language/Docker/Syntax.hs, dist/build/Language/Docker/Syntax.o )
[ 3 of 11] Compiling Language.Docker.Syntax.Lift ( src/Language/Docker/Syntax/Lift.hs, dist/build/Language/Docker/Syntax/Lift.o )
[ 4 of 11] Compiling Language.Docker.PrettyPrint ( src/Language/Docker/PrettyPrint.hs, dist/build/Language/Docker/PrettyPrint.o )

src/Language/Docker/PrettyPrint.hs:91:25: error:
    Not in scope: ‘fail’
    Perhaps you want to add ‘fail’ to the import list in the import of
    ‘Prelude’ (src/Language/Docker/PrettyPrint.hs:(15,1)-(17,18)).
[ 5 of 11] Compiling Language.Docker.Normalize ( src/Language/Docker/Normalize.hs, dist/build/Language/Docker/Normalize.o )
[ 6 of 11] Compiling Language.Docker.Lexer ( src/Language/Docker/Lexer.hs, dist/build/Language/Docker/Lexer.o )
[ 7 of 11] Compiling Language.Docker.Parser ( src/Language/Docker/Parser.hs, dist/build/Language/Docker/Parser.o )
[ 8 of 11] Compiling Language.Docker.EDSL.Types ( src/Language/Docker/EDSL/Types.hs, dist/build/Language/Docker/EDSL/Types.o )

Add --link flag to COPY/ADD instrructions

Hello,

On 9 March, a new version of dockerfile frontend has been released. This introduced one change to the syntax.

COPY --link and ADD --link allow copying files with increased cache efficiency and rebase images without requiring them to be rebuilt. --link copies files to a separate layer and then uses new LLB MergeOp implementation to chain independent layers together.

Documentation: https://github.com/moby/buildkit/blob/dockerfile/1.4.0/frontend/dockerfile/docs/syntax.md#linked-copies-copy---link-add---link

We need to add support for the following new syntaxes:

COPY --link /source /target
ADD --link /source /target
COPY --link=true /source /target
COPY --link=false /source /target

Best regards,

Build failure with ghc 8.4

[ 5 of 13] Compiling Language.Docker.PrettyPrint ( src/Language/Docker/PrettyPrint.hs, dist/build/Language/Docker/PrettyPrint.o )

src/Language/Docker/PrettyPrint.hs:34:64: error:
    Ambiguous occurrence ‘<>’
    It could refer to either ‘Prelude.<>’,
                             imported from ‘Prelude’ at src/Language/Docker/PrettyPrint.hs:14:1-43
                             (and originally defined in ‘GHC.Base’)
                          or ‘Text.PrettyPrint.<>’,
                             imported from ‘Text.PrettyPrint’ at src/Language/Docker/PrettyPrint.hs:15:1-23
                             (and originally defined in ‘Text.PrettyPrint.HughesPJ’)
   |
34 | prettyPrintImage (Image (Just (Registry reg)) name) = text reg <> char '/' <> text name
   |                                                                ^^

The same failure repeats many, many times, every time <> is used.

Pretty printing of `RUN` in shell form replaces multiple spaces in quoted string

First of all, thanks for this helpful library.

The Dockerfile

FROM alpine
RUN echo 'a    b'

is pretty printed by prettyPrint as

FROM alpine
RUN echo 'a b'

I'd expect a quoted string to be left unchanged instead of reduced to 'a b'.

This happens not only with RUN instructions (in shell form), but also with health check commands.

A minimal code example is

module Main
  ( main
  ) where

import qualified Data.Either as Either
import qualified Data.Text as T
import qualified Data.Text.IO as T.IO
import qualified Data.Text.Lazy as Lazy
import qualified Language.Docker as Docker
import Prelude (IO, ($))

main :: IO ()
main = T.IO.putStr prettyPrinted
  where
    prettyPrinted = Lazy.toStrict $ Docker.prettyPrint parsed
    parsed = Either.fromRight [] $ Docker.parseText source
    source = T.pack "FROM alpine\nRUN echo 'a    b'\n"

I'm using version 9.1.1 of the library (via LTS Haskell 16.18).

Fail to compile language-docker-5.0.1

Hey,
I am trying to build hadolint on my gentoo system, but I can not manage to build,
language-docker-5.0.1, here the error I get

[ 1 of 11] Compiling Paths_language_docker ( dist/build/autogen/Paths_language_docker.hs, dist/build/Paths_language_docker.o )
[ 2 of 11] Compiling Language.Docker.Syntax ( src/Language/Docker/Syntax.hs, dist/build/Language/Docker/Syntax.o )
[ 3 of 11] Compiling Language.Docker.Syntax.Lift ( src/Language/Docker/Syntax/Lift.hs, dist/build/Language/Docker/Syntax/Lift.o )
[ 4 of 11] Compiling Language.Docker.PrettyPrint ( src/Language/Docker/PrettyPrint.hs, dist/build/Language/Docker/PrettyPrint.o )

src/Language/Docker/PrettyPrint.hs:93:25: error:
    Not in scope: ‘fail’
    Perhaps you want to add ‘fail’ to the import list in the import of
    ‘Prelude’ (src/Language/Docker/PrettyPrint.hs:(15,1)-(17,18)).
[ 5 of 11] Compiling Language.Docker.Normalize ( src/Language/Docker/Normalize.hs, dist/build/Language/Docker/Normalize.o )
[ 6 of 11] Compiling Language.Docker.Lexer ( src/Language/Docker/Lexer.hs, dist/build/Language/Docker/Lexer.o )
[ 7 of 11] Compiling Language.Docker.Parser ( src/Language/Docker/Parser.hs, dist/build/Language/Docker/Parser.o )

src/Language/Docker/Parser.hs:402:17: warning: [-Wmissing-monadfail-instances]
    • No instance for (Control.Monad.Fail.MonadFail
                         (ParsecT String () Data.Functor.Identity.Identity))
        arising from the failable pattern ‘Cmd checkCommand’
        (this will become an error in a future GHC release)
    • In a stmt of a 'do' block: Cmd checkCommand <- cmd
      In the expression:
        do { Cmd checkCommand <- cmd;
             let interval = listToMaybe intervals;
             let timeout = listToMaybe timeouts;
             let startPeriod = listToMaybe startPeriods;
             .... }
      In a case alternative:
          _ -> do { Cmd checkCommand <- cmd;
                    let interval = ...;
                    let timeout = ...;
                    .... }
[ 8 of 11] Compiling Language.Docker.EDSL.Types ( src/Language/Docker/EDSL/Types.hs, dist/build/Language/Docker/EDSL/Types.o )

I use ghc version 8.0.2 should I try a newer version ?

Digested image is not parsed

There is a definition for DigestedImage but it is not used. I always get it as TaggedImage

> parseString "FROM ubuntu@sha256:0ef2e08ed3fabfc44002ccb846c4f2416a2135affc3ce39538834059606f32dd"
Right [InstructionPos (From (TaggedImage "ubuntu@sha256" "0ef2e08ed3fabfc44002ccb846c4f2416a2135affc3ce39538834059606f32dd")) "<string>" 1]

Cannot parse if line ends with linebreak and space

FROM centos:centos7

RUN yum -y install --setopt=tsflags=nodocs epel-release && \ 
    yum -y update && yum clean all

There is a space after \

$ integration-tests/parseFile Dockerfile.break
Left (TrivialError (SourcePos {sourceName = "Dockerfile.break", sourceLine = Pos 4, sourceColumn = Pos 5} :| []) (Just (Tokens ('y' :| ""))) (fromList [Tokens ('#' :| ""),Label ('A' :| "DD"),Label ('A' :| "RG"),Label ('C' :| "MD"),Label ('C' :| "OPY"),Label ('E' :| "NTRYPOINT"),Label ('E' :| "NV"),Label ('E' :| "XPOSE"),Label ('F' :| "ROM"),Label ('H' :| "EALTHCHECK"),Label ('L' :| "ABEL"),Label ('M' :| "AINTAINER"),Label ('O' :| "NBUILD"),Label ('R' :| "UN"),Label ('S' :| "HELL"),Label ('S' :| "TOPSIGNAL"),Label ('U' :| "SER"),Label ('V' :| "OLUME"),Label ('W' :| "ORKDIR"),Label ('t' :| "he rest of a new line followed by the next instruction"),EndOfInput]))

It wasn't catched in integration tests because error message has changed

if ./parseFile "$dockerfile" | grep -a1 unexpected; then

build errors with ghc-9.8.x

[   30s] src/Language/Docker.hs:26:5: error: [GHC-97219]
[   30s]     Duplicate record field ‘targetPath’ in export list:
[   30s]        ‘Language.Docker.Syntax.AddArgs(..)’ exports the field ‘Language.Docker.Syntax.targetPath’
[   30s]        belonging to the constructor ‘Language.Docker.Syntax.AddArgs’
[   30s]          imported qualified from ‘Language.Docker.Syntax’ at src/Language/Docker.hs:46:1-39
[   30s]          (and originally defined at src/Language/Docker/Syntax.hs:194:9-18)
[   30s]        ‘Language.Docker.Syntax.CopyArgs(..)’ exports the field ‘Language.Docker.Syntax.targetPath’
[   30s]        belonging to the constructor ‘Language.Docker.Syntax.CopyArgs’
[   30s]          imported qualified from ‘Language.Docker.Syntax’ at src/Language/Docker.hs:46:1-39
[   30s]          (and originally defined at src/Language/Docker/Syntax.hs:175:9-18)
[   30s]     Suggested fix: Perhaps you intended to use DuplicateRecordFields
[   30s]    |
[   30s] 26 |     Language.Docker.Syntax.AddArgs (..),
[   30s]    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[   30s] 
[   30s] src/Language/Docker.hs:26:5: error: [GHC-97219]
[   30s]     Duplicate record field ‘sourcePaths’ in export list:
[   30s]        ‘Language.Docker.Syntax.AddArgs(..)’ exports the field ‘Language.Docker.Syntax.sourcePaths’
[   30s]        belonging to the constructor ‘Language.Docker.Syntax.AddArgs’
[   30s]          imported qualified from ‘Language.Docker.Syntax’ at src/Language/Docker.hs:46:1-39
[   30s]          (and originally defined at src/Language/Docker/Syntax.hs:193:9-19)
[   30s]        ‘Language.Docker.Syntax.CopyArgs(..)’ exports the field ‘Language.Docker.Syntax.sourcePaths’
[   30s]        belonging to the constructor ‘Language.Docker.Syntax.CopyArgs’
[   30s]          imported qualified from ‘Language.Docker.Syntax’ at src/Language/Docker.hs:46:1-39
[   30s]          (and originally defined at src/Language/Docker/Syntax.hs:174:9-19)
[   30s]     Suggested fix: Perhaps you intended to use DuplicateRecordFields
[   30s]    |
[   30s] 26 |     Language.Docker.Syntax.AddArgs (..),
[   30s]    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[   30s] [18 of 18] Compiling Paths_language_docker
[   30s] error: Bad exit status from /var/tmp/rpm-tmp.Dg6asp (%build)

Inaccurate version bounds

As can be seen at

there were a few issues due to inaccurate and/or missing version bounds which affect reverse dependencies of language-docker. I've already manually fixed up those up for you via Hackage metadata revision, see e.g.

and you can review the resulting report at

Going forward, we need your help! Please help us ensure a good user experience for users of Hackage/Cabal by the use of the PVP mandated lower&upper bounds in order to reduce the penalty on the Hackage infrastructure as well as to avoid unnecessary extra work for you as well as for us Hackage Trustees!

sdist doesn't include all necessary files for tests

Running 1 test suites...
Test suite hspec: RUNNING...

Language.Docker.Integration
  parse file
FROM foo:7-slim
#  An extra space after the env value should be no problem
ENV container="false container2=true"
ENV A="a.sh" \
    D="c B=installDBBinaries.sh"
ENV X="\"Y\" Z"
ENV DOG="Rex The Dog CAT=Top Cat"
ENV DOCKER_TLS_CERTDIR=""
ENV foo a="afoo bar bazqu\"z"
ENV BASE_PATH="/var/spool/apt-mirror"
    1.Dockerfile
FROM scratch
RUN set -ex; apt-get update; if ! which gpg; then apt-get install -y --no-install-recommends gnupg; fi; if ! gpg --version | grep -q '^gpg (GnuPG) 1\.'; then apt-get install -y --no-install-recommends dirmngr; fi; rm -rf /var/lib/apt/lists/*
ONBUILD ENV RUNNER_CMD_EXEC="${RUNNER_CMD_EXEC:-java \$JAVA_OPTS -jar /runtime/server.jar \$JAR_OPTS}"
ENV BUNDLE_WITHOUT="${bundle_without:-development test}"
    2.Dockerfile
    3.Dockerfile FAILED [1]
    4.Dockerfile FAILED [2]
    5.Dockerfile FAILED [3]
  escape character detection logic
    ensure the pretty printer respects escape pragmas FAILED [4]
    ensure escape character '\' is used as default FAILED [5]
    ensure the printer ignores escape pragmas in the wrong place FAILED [6]
  parse text
    1.Dockerfile
    2.Dockerfile
    3.Dockerfile FAILED [7]
    4.Dockerfile FAILED [8]
    5.Dockerfile FAILED [9]
    1.Dockerfile crlf
    2.Dockerfile crlf
    3.Dockerfile crlf FAILED [10]
    4.Dockerfile crlf FAILED [11]
    5.Dockerfile crlf FAILED [12]
Language.Docker.ParsePragma
  parse # pragma
    # escape = \
    #escape=`
    # escape=`
    #escape =`
    #escape= `
    # escape =`
    #escape = `
    # escape = `
    # Escape = `
    # ESCAPE = `
    #syntax=docker/dockerfile:1.0
    # syntax=docker/dockerfile:1.0
    #syntax =docker/dockerfile:1.0
    #syntax= docker/dockerfile:1.0
    # syntax =docker/dockerfile:1.0
    #syntax = docker/dockerfile:1.0
    # syntax= docker/dockerfile:1.0
    # syntax = docker/dockerfile:1.0
    # Syntax = docker/dockerfile:1.0
    # SYNTAX = docker/dockerfile:1.0
Language.Docker.Parser
  parse ARG
    no default
    no default with =
    with default
  parse FROM
    parse untagged image
    parse tagged image
    parse digested image
    parse digested image with tag
    parse image with spaces at the end
  parse aliased FROM
    parse untagged image
    parse tagged image
    parse diggested image
  parse FROM with platform
    parse untagged image with platform
    parse tagged image with platform
  parse FROM with registry
    registry without port
    parse with port and tag
    Not a registry if no TLD
  parse LABEL
    parse label
    parse space separated label
    parse quoted labels
    parses multiline labels
  parse ENV
    parses unquoted pair
    parse with space between key and value
    parse with more then one (white)space between key and value
    parse quoted value pair
    parse multiple unquoted pairs
    parse multiple quoted pairs
    env works before cmd
    parse with two spaces between
    have envs on multiple lines
    parses long env over multiple lines
    parse single var list
    parses many env lines with an equal sign in the value
    parses many env lines in mixed style
    parses many env with backslashes
    parses env with % in them
    parses env with % in them
  parse RUN
    escaped with space before
    does not choke on unmatched brackets
    Distinguishes between text and a list
    Accepts spaces inside the brackets
  parse CMD
    one line cmd
    cmd over several lines
    quoted command params
    Parses commas correctly
  parse SHELL
    quoted shell params
  parse HEALTHCHECK
    parse healthcheck with interval
    parse healthcheck with retries
    parse healthcheck with timeout
    parse healthcheck with start-period
    parse healthcheck with all flags
    parse healthcheck with no flags
  parse MAINTAINER
    maintainer of untagged scratch image
    maintainer with mail
    maintainer only mail after from
  parse # comment
    multiple comments before run
    multiple comments after run
    empty comment
    many escaped lines
    accepts backslash inside string
    tolerates spaces after a newline escape
    Correctly joins blank lines starting with comments
  expose
    should handle number ports
    should handle many number ports
    should handle ports with protocol
    should handle ports with protocol and variables
    should handle port ranges
    should handle udp port ranges
    should handle multiline variables
  syntax
    should handle lowercase instructions (#7 - https://github.com/beijaflor-io/haskell-language-dockerfile/issues/7)
  ADD
    simple ADD
    multifiles ADD
    list of quoted files
    with chown flag
    with chmod flag
    with chown and chmod flag
    with chown and chmod flag other order
    list of quoted files and chown
  COPY
    simple COPY
    multifiles COPY
    list of quoted files
    with chown flag
    with chmod flag
    with from flag
    with all three flags
    with all three flags in different order
    supports windows paths
    does not get confused with trailing whitespace
  RUN with experimental flags
    --mount=type=bind and target
    --mount default to bind
    --mount=type=bind all modifiers
    --mount=type=cache with target
    --mount=type=cache with all modifiers
    --mount=type=tmpfs
    --mount=type=ssh
    --mount=type=ssh,required=false
    --mount=type=ssh,required=False
    --mount=type=secret,required=true
    --mount=type=secret,required=True
    --mount=type=ssh all modifiers
    --mount=type=ssh all modifiers, required explicit
    --mount=type=secret all modifiers
    --mount=type=secret all modifiers, required explicit
    --network=none
    --network=host
    --network=default
    --security=insecure
    --security=sandbox
    allows all flags
Language.Docker.PrettyPrint
  pretty print ADD
    with just copy
    with just chown
    with just chmod
    with both chown and chmod
  pretty print COPY
    with just copy
    with just chown
    with just chmod
    with source baseimage
    with both chown and chmod
    with all three: from, chown and chmod
  pretty print # escape
    # escape = \
    # escape = `
  pretty print # syntax
    # syntax = docker/dockerfile:1.0

Failures:

  test/Language/Docker/IntegrationSpec.hs:27:5:
  1) Language.Docker.Integration, parse file, 3.Dockerfile
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/3.Dockerfile: openBinaryFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse file/3.Dockerfile/"

  test/Language/Docker/IntegrationSpec.hs:33:5:
  2) Language.Docker.Integration, parse file, 4.Dockerfile
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/4.Dockerfile: openBinaryFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse file/4.Dockerfile/"

  test/Language/Docker/IntegrationSpec.hs:39:5:
  3) Language.Docker.Integration, parse file, 5.Dockerfile
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/5.Dockerfile: openBinaryFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse file/5.Dockerfile/"

  test/Language/Docker/IntegrationSpec.hs:46:5:
  4) Language.Docker.Integration, escape character detection logic, ensure the pretty printer respects escape pragmas
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/6.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/escape character detection logic/ensure the pretty printer respects escape pragmas/"

  test/Language/Docker/IntegrationSpec.hs:55:5:
  5) Language.Docker.Integration, escape character detection logic, ensure escape character '\' is used as default
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/7.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/escape character detection logic/ensure escape character '\\' is used as default/"

  test/Language/Docker/IntegrationSpec.hs:64:5:
  6) Language.Docker.Integration, escape character detection logic, ensure the printer ignores escape pragmas in the wrong place
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/8.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/escape character detection logic/ensure the printer ignores escape pragmas in the wrong place/"

  test/Language/Docker/IntegrationSpec.hs:86:5:
  7) Language.Docker.Integration, parse text, 3.Dockerfile
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/3.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse text/3.Dockerfile/"

  test/Language/Docker/IntegrationSpec.hs:92:5:
  8) Language.Docker.Integration, parse text, 4.Dockerfile
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/4.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse text/4.Dockerfile/"

  test/Language/Docker/IntegrationSpec.hs:98:5:
  9) Language.Docker.Integration, parse text, 5.Dockerfile
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/5.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse text/5.Dockerfile/"

  test/Language/Docker/IntegrationSpec.hs:116:5:
  10) Language.Docker.Integration, parse text, 3.Dockerfile crlf
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/3.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse text/3.Dockerfile crlf/"

  test/Language/Docker/IntegrationSpec.hs:122:5:
  11) Language.Docker.Integration, parse text, 4.Dockerfile crlf
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/4.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse text/4.Dockerfile crlf/"

  test/Language/Docker/IntegrationSpec.hs:128:5:
  12) Language.Docker.Integration, parse text, 5.Dockerfile crlf
       uncaught exception: IOException of type NoSuchThing
       test/fixtures/5.Dockerfile: openFile: does not exist (No such file or directory)

  To rerun use: --match "/Language.Docker.Integration/parse text/5.Dockerfile crlf/"

Randomized with seed 1785490773

Finished in 0.0141 seconds
159 examples, 12 failures
Test suite hspec: FAIL
Test suite logged to: dist/test/language-docker-10.0.0-hspec.log
0 of 1 test suites (0 of 1 test cases) passed.

Parse RUN agruments do not respect control operators

Intro

bash manpage:

       control operator
              A token that performs a control function.  It is one of the following symbols:
              || & && ; ;; ( ) | |& <newline>

Control operators are important for decisions which are happening later in hadolint https://github.com/hadolint/hadolint/blob/master/src/Hadolint/Rules.hs#L173

This issue has been found through hadolint/hadolint#196

Current state

RUN set -ex; apk add --no-cache curl=7.59.0-r0

is parsed

$ stack repl
> parseString "RUN set -ex; apk add --no-cache ca-certificates=20171114-r0"
Right [InstructionPos {instruction = Run (Arguments ["set","-ex;","apk","add","--no-cache","ca-certificates=20171114-r0"]), sourcename = "<string>", lineNumber = 1}]

where ; is parsed as part of -ex.

I believe it is done here

return $ Arguments (words args)

Desired state

Split RUN arguments using white space and selected control operators.

$ stack repl
> parseString "RUN set -ex; apk add --no-cache ca-certificates=20171114-r0"
Right [InstructionPos {instruction = Run (Arguments ["set","-ex",";","apk","add","--no-cache","ca-certificates=20171114-r0"]), sourcename = "<string>", lineNumber = 1}]

GHC 8.10.1 build issues

Currently, language-docker-0.3.6.0 does not build GHC 8.10.1. This issue aims to document the underlying issues.

  1. The first issue you run into is this error:

    [ 7 of 10] Compiling Language.Docker.Syntax.Lift ( src/Language/Docker/Syntax/Lift.hs, interpreted )
    
    src/Language/Docker/Syntax/Lift.hs:16:1: error:
        Illegal kind signature: ‘a_a8rK’
          Perhaps you intended to use KindSignatures
        In an instance declaration
       |
    16 | deriveLift ''Fixed
       | ^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:16:1: error:
        Unexpected kind variable ‘k_a8rJ’
        Perhaps you intended to use PolyKinds
        In an instance declaration
       |
    16 | deriveLift ''Fixed
       | ^^^^^^^^^^^^^^^^^^
    

    This is because Fixed is poly-kinded as of base-4.14. A simple fix is to enable PolyKinds at the top of Language.Docker.Syntax.Lift.

  2. The next issue involves a missing Lift Text instance:

    [ 7 of 10] Compiling Language.Docker.Syntax.Lift ( src/Language/Docker/Syntax/Lift.hs, interpreted )
    
    src/Language/Docker/Syntax/Lift.hs:23:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_akQJ)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "PortStr"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_akQJ)
          In a case alternative:
              PortStr x0_akQJ
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "PortStr"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_akQJ)
       |
    23 | deriveLift ''Port
       | ^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:27:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_akSf)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "Registry"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_akSf)
          In a case alternative:
              Registry x0_akSf
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "Registry"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_akSf)
       |
    27 | deriveLift ''Registry
       | ^^^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:29:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x1_akSS)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               ((Language.Haskell.TH.Lib.Internal.appE
                   (Language.Haskell.TH.Lib.Internal.conE
                      ((Language.Haskell.TH.Syntax.Name
                          (Language.Haskell.TH.Syntax.mkOccName "Image"))
                         (((Language.Haskell.TH.Syntax.NameG
                              Language.Haskell.TH.Syntax.DataName)
                             (Language.Haskell.TH.Syntax.mkPkgName
                                "language-docker-8.1.0-inplace"))
                            (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                  (lift x0_akSR)))
              (lift x1_akSS)
          In a case alternative:
              Image x0_akSR x1_akSS
                -> (Language.Haskell.TH.Lib.Internal.appE
                      ((Language.Haskell.TH.Lib.Internal.appE
                          (Language.Haskell.TH.Lib.Internal.conE
                             ((Language.Haskell.TH.Syntax.Name
                                 (Language.Haskell.TH.Syntax.mkOccName "Image"))
                                (((Language.Haskell.TH.Syntax.NameG
                                     Language.Haskell.TH.Syntax.DataName)
                                    (Language.Haskell.TH.Syntax.mkPkgName
                                       "language-docker-8.1.0-inplace"))
                                   (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                         (lift x0_akSR)))
                     (lift x1_akSS)
       |
    29 | deriveLift ''Image
       | ^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:31:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_akTx)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "ImageAlias"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_akTx)
          In a case alternative:
              ImageAlias x0_akTx
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "ImageAlias"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_akTx)
       |
    31 | deriveLift ''ImageAlias
       | ^^^^^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:33:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_akU9)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "Tag"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_akU9)
          In a case alternative:
              Tag x0_akU9
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "Tag"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_akU9)
       |
    33 | deriveLift ''Tag
       | ^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:35:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_akUL)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "Digest"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_akUL)
          In a case alternative:
              Digest x0_akUL
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "Digest"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_akUL)
       |
    35 | deriveLift ''Digest
       | ^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:37:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x4_akVr)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               ((Language.Haskell.TH.Lib.Internal.appE
                   ((Language.Haskell.TH.Lib.Internal.appE
                       ((Language.Haskell.TH.Lib.Internal.appE
                           ((Language.Haskell.TH.Lib.Internal.appE
                               (Language.Haskell.TH.Lib.Internal.conE
                                  ((Language.Haskell.TH.Syntax.Name
                                      (Language.Haskell.TH.Syntax.mkOccName "BaseImage"))
                                     (((Language.Haskell.TH.Syntax.NameG
                                          Language.Haskell.TH.Syntax.DataName)
                                         (Language.Haskell.TH.Syntax.mkPkgName
                                            "language-docker-8.1.0-inplace"))
                                        (Language.Haskell.TH.Syntax.mkModName
                                           "Language.Docker.Syntax")))))
                              (lift x0_akVn)))
                          (lift x1_akVo)))
                      (lift x2_akVp)))
                  (lift x3_akVq)))
              (lift x4_akVr)
          In a case alternative:
              BaseImage x0_akVn x1_akVo x2_akVp x3_akVq x4_akVr
                -> (Language.Haskell.TH.Lib.Internal.appE
                      ((Language.Haskell.TH.Lib.Internal.appE
                          ((Language.Haskell.TH.Lib.Internal.appE
                              ((Language.Haskell.TH.Lib.Internal.appE
                                  ((Language.Haskell.TH.Lib.Internal.appE
                                      (Language.Haskell.TH.Lib.Internal.conE
                                         ((Language.Haskell.TH.Syntax.Name
                                             (Language.Haskell.TH.Syntax.mkOccName "BaseImage"))
                                            (((Language.Haskell.TH.Syntax.NameG
                                                 Language.Haskell.TH.Syntax.DataName)
                                                (Language.Haskell.TH.Syntax.mkPkgName
                                                   "language-docker-8.1.0-inplace"))
                                               (Language.Haskell.TH.Syntax.mkModName
                                                  "Language.Docker.Syntax")))))
                                     (lift x0_akVn)))
                                 (lift x1_akVo)))
                             (lift x2_akVp)))
                         (lift x3_akVq)))
                     (lift x4_akVr)
       |
    37 | deriveLift ''BaseImage
       | ^^^^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:41:1: error:
        • Could not deduce (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
          from the context: Lift args
            bound by the instance declaration
            at src/Language/Docker/Syntax/Lift.hs:41:1-24
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_akYg)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "User"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_akYg)
          In a case alternative:
              User x0_akYg
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "User"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_akYg)
       |
    41 | deriveLift ''Instruction
       | ^^^^^^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:43:1: error:
        • Could not deduce (Lift Filename) arising from a use of ‘lift’
          from the context: Lift args
            bound by the instance declaration
            at src/Language/Docker/Syntax/Lift.hs:43:1-27
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x1_al0d)’
          In the first argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘((Language.Haskell.TH.Lib.Internal.appE
                 ((Language.Haskell.TH.Lib.Internal.appE
                     (Language.Haskell.TH.Lib.Internal.conE
                        ((Language.Haskell.TH.Syntax.Name
                            (Language.Haskell.TH.Syntax.mkOccName "InstructionPos"))
                           (((Language.Haskell.TH.Syntax.NameG
                                Language.Haskell.TH.Syntax.DataName)
                               (Language.Haskell.TH.Syntax.mkPkgName
                                  "language-docker-8.1.0-inplace"))
                              (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                    (lift x0_al0c)))
                (lift x1_al0d))’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               ((Language.Haskell.TH.Lib.Internal.appE
                   ((Language.Haskell.TH.Lib.Internal.appE
                       (Language.Haskell.TH.Lib.Internal.conE
                          ((Language.Haskell.TH.Syntax.Name
                              (Language.Haskell.TH.Syntax.mkOccName "InstructionPos"))
                             (((Language.Haskell.TH.Syntax.NameG
                                  Language.Haskell.TH.Syntax.DataName)
                                 (Language.Haskell.TH.Syntax.mkPkgName
                                    "language-docker-8.1.0-inplace"))
                                (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                      (lift x0_al0c)))
                  (lift x1_al0d)))
              (lift x2_al0e)
       |
    43 | deriveLift ''InstructionPos
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:45:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_al12)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "SourcePath"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_al12)
          In a case alternative:
              SourcePath x0_al12
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "SourcePath"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_al12)
       |
    45 | deriveLift ''SourcePath
       | ^^^^^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:47:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_al1E)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "TargetPath"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_al1E)
          In a case alternative:
              TargetPath x0_al1E
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "TargetPath"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_al1E)
       |
    47 | deriveLift ''TargetPath
       | ^^^^^^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:49:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_al2g)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "Chown"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_al2g)
          In a case alternative:
              Chown x0_al2g
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "Chown"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_al2g)
       |
    49 | deriveLift ''Chown
       | ^^^^^^^^^^^^^^^^^^
    
    src/Language/Docker/Syntax/Lift.hs:51:1: error:
        • No instance for (Lift Data.Text.Internal.Text)
            arising from a use of ‘lift’
        • In the second argument of ‘Language.Haskell.TH.Lib.Internal.appE’, namely
            ‘(lift x0_al2S)’
          In the expression:
            (Language.Haskell.TH.Lib.Internal.appE
               (Language.Haskell.TH.Lib.Internal.conE
                  ((Language.Haskell.TH.Syntax.Name
                      (Language.Haskell.TH.Syntax.mkOccName "CopySource"))
                     (((Language.Haskell.TH.Syntax.NameG
                          Language.Haskell.TH.Syntax.DataName)
                         (Language.Haskell.TH.Syntax.mkPkgName
                            "language-docker-8.1.0-inplace"))
                        (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
              (lift x0_al2S)
          In a case alternative:
              CopySource x0_al2S
                -> (Language.Haskell.TH.Lib.Internal.appE
                      (Language.Haskell.TH.Lib.Internal.conE
                         ((Language.Haskell.TH.Syntax.Name
                             (Language.Haskell.TH.Syntax.mkOccName "CopySource"))
                            (((Language.Haskell.TH.Syntax.NameG
                                 Language.Haskell.TH.Syntax.DataName)
                                (Language.Haskell.TH.Syntax.mkPkgName
                                   "language-docker-8.1.0-inplace"))
                               (Language.Haskell.TH.Syntax.mkModName "Language.Docker.Syntax")))))
                     (lift x0_al2S)
       |
    51 | deriveLift ''CopySource
       | ^^^^^^^^^^^^^^^^^^^^^^^
    

    #50 incorrectly attempted to fix this by limiting the build plan to only GHC 8.8 or later. However, that is insufficient, as GHC 8.10.1 bundles text-1.2.3.2, which does not define a Lift Text instance. (See here for more discussion on this.)

    I think the best way to solve this problem would be to depend on th-lift-instances, which backports the Lift Text instance to older versions of text. Since language-dockerfile already depends on this library, this doesn't seem like that radical of a change. If we did this, then language-docker could even theoretically support older versions of GHC again, although I will leave that choice to the maintainers of this library.

prettyPrint does not preseve the content of quoted string

Hi there,

I have a similar problem to #57 with the prettyPrint for the quoted string.
It is about prettyPrint automatically move the parts after token && to a new line even in a quoted string.
The example code is described as below.

Is there any quick workarounds for this issue? I'm really looking forward to it.
Thank you very much!

I am using language-docker-10.4.2.

module Main
  ( main
  ) where

import qualified Data.Either as Either
import qualified Data.Text as T
import qualified Data.Text.IO as T.IO
import qualified Data.Text.Lazy as Lazy
import qualified Language.Docker as Docker
import Prelude (IO, ($))

main :: IO ()
main = T.IO.putStr prettyPrinted
  where
    prettyPrinted = Lazy.toStrict $ Docker.prettyPrint parsed
    parsed = Either.fromRight [] $ Docker.parseText source
    source = T.pack "FROM alpine\nRUN grep -F 'cd dir && ./run.sh ' /opt/Makefile"

It should return

FROM alpine
RUN grep -F 'cd dir && ./run.sh ' /opt/Makefile

However, the actual return is as below, which creates different shell execution result.

FROM alpine
RUN grep -F 'cd dir \
 && ./run.sh ' /opt/Makefile

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.