Scalafmt with Docker

Ian Hellström | 4 March 2020 | 2 min read

Scalafmt is a popular formatter for Scala. The formatting it produces is not always identical across versions, even with the same configuration file. To ensure all developers on a team format the code in the same way, I’ll show you how to roll your own Scalafmt action container with Docker.

All of my Scala projects have a .scalafmt.conf file in the root to ensure consistent formatting:

style = "IntelliJ"
version = "2.4.2"
maxColumn = 100
newlines.alwaysBeforeTopLevelStatements = true
newlines.penalizeSingleSelectMultiArgList = false
rewrite.rules = [
  "RedundantBraces"
  "RedundantParens"
  "SortImports"
  "PreferCurlyFors" ]
rewrite.redundantBraces.stringInterpolation = truene
danglingParentheses = false
spaces.afterTripleEquals = true
align.openParenDefnSite = true
align.openParenCallSite = false
literals.long = Upper
literals.float = Lower
literals.double = Upper

Although it lists a specific version, I have noticed that different Scalafmt versions can produce different results; version is as of this writing not used anywhere. There is now an edition option, but that has not always been there. So, let’s build a Docker image instead.

Dockerfile

The following Docker configuration can be used to create an action container for the scalafmt command:

FROM alpine:3.11.3

RUN apk --no-cache add curl openjdk11-jre-headless

WORKDIR /scripts

RUN curl -Lo coursier https://git.io/coursier-cli && chmod +x /scripts/coursier
RUN /scripts/coursier bootstrap org.scalameta:scalafmt-cli_2.12:2.4.2 \
      -r sonatype:snapshots \
      -o /usr/local/bin/scalafmt \
      --standalone \
      --main org.scalafmt.cli.Cli
RUN rm -f /scripts/coursier

ENTRYPOINT ["scalafmt"]

It uses a small Linux base image and follows the installation instructions. The headless JRE is installed because scalafmt is a command-line tool, so it does not need any graphical components. There is no need to grab the JDK either.

To build the image, execute docker build -t scalafmt:2.4.2 $(pwd). Please note that the tag matches the Scalafmt version.

Alias

For development on your own machine, I recommend an alias:

alias scalafmt='docker run --rm -w $(pwd) -v $(pwd):$(pwd) scalafmt:2.4.2 --exclude target'

That way, you can run scalafmt from a project’s root, as if you had Scalafmt installed locally. It automatically excludes the target directory. If, however, you want to exclude more or different directories, add those to the alias itself or leave off --exclude target altogether.

Whenever the team decides to upgrade to a newer Scalafmt version, you can

  1. Rebuild the image;
  2. Push it to a container registry;
  3. Have everyone update their aliases.

That way, everyone is guaranteed to produce the same formatting.

You may wonder why I do not suggest the native image instead. Unfortunately, it does not yet work on Windows, so it’s not portable. More importantly, I never got it to work on my machine. Docker (almost) always works.