Dockerfmt: A Dockerfile Formatter
66 comments
·April 9, 2025jensenbox
PhilippGille
> the easiest way would be to run it as a Docker container
Regarding this part, you can always just run a base image and add the app yourself. I'm on mobile so can't test, but should be along these lines:
docker run --rm --name dockerfmt \
-v /path/to/Dockerfile:/tmp/Dockerfile \
golang:1.24-alpine sh -c \
"apk add git && go run github.com/reteps/dockerfmt@latest /tmp/Dockerfile"
> against an existing fileFor this part yes, you'd still need one, but it can be any of your own.
spicypete
Hi there — I’ll try to distribute a docker release of the binary tomorrow!
ithkuil
I suggest you build it with https://github.com/ko-build/ko
so you can still have no dockerfile and the irony is not ruined
IshKebab
I think they were asking for a sample Dockerfile in the repo to test it on.
spicypete
Then they can use one of the 14 in the `tests` directory :) https://github.com/reteps/dockerfmt/tree/main/tests
klysm
The project should certainly also be formatting its own docker file via a docker invocation
thedougd
And built in a multistage Dockerfile.
revskill
The author doesn't know how to use AI.
krick
I usually share the sentiment, but, come on, it's packaged as a single binary file...
mdaniel
I don't want to vouch for the flagged and dead comment https://news.ycombinator.com/item?id=43630653 because I suspect it was killed for its tone but wow it is really illustrative of the QA that didn't go into this product
grandempire
You aren’t a real software engineer if your project doesn’t have 50 dot files in the root for your formatters, package mangers, linters, and ci.
Who formats the formatter configs?
zufallsheld
> Who formats the formatter configs?
Other formatters of course since the configs are often yaml, toml, ini or json.
mdaniel
waaaat? https://github.com/reteps/dockerfmt#:~:text=The%20RUN%20pars...
I am firmly in the camp of
RUN set -e ;\
export DEBIAN_FRONTEND=noninteractive ;\
etc etc
so I guess this tool isn't for mergovostes
As far as I can tell from https://github.com/moby/moby/issues/4032, as of Debian 12 "bookworm" and Ubuntu 23.04 "Lunar", explicitly setting DEBIAN_FRONTEND is no longer necessary.
spicypete
Is there any reason you prefer `set -e` over `&&`? I'm curious if this is a readability thing.
figmert
I'm firmly in that camp but I also always add `set -eux`, which makes it so much better at debugging as that gives you individual commands it runs before the output of them.
figmert
To be clear, the difference is something along this line:
$ bash -ec 'echo hello && ls -la /tmp/ | grep systemd && false && echo testing'
hello
drwx------. 3 root root 60 Mar 29 18:33 systemd-private-bluetooth.service-yuSMVM
drwx------. 3 root root 60 Mar 29 18:33 systemd-private-upower.service-YhHHP2
versus $ bash -euxc 'echo hello; ls -la /tmp/ | grep systemd; false; echo testing'
+ echo hello
hello
+ ls -la /tmp/
+ grep systemd
drwx------. 3 root root 60 Mar 29 18:33 systemd-private-bluetooth.service-yuSMVM
drwx------. 3 root root 60 Mar 29 18:33 systemd-private-upower.service-YhHHP2
+ false
Docker also supports the `SHELL` syntax now, which is even better, because you can set it once at the top of the Dockerfile without having to do the whole `set -eux` on every line.mdaniel
Readability is putting it mildly; do you write your shell scripts using that && style? No? Why not, is it for readability?
I also have a hard time reasoning about && with anything other than the most braindead sequence of commands because: $(thing && if other_thing; then inner_thing1 && thing2; fi && ohgawd)
And I just realized while typing that out that if its parser doesn't support ; then I guess one needs to
RUN if conditional_thing \
then good_luck && \
fi && \
echo "whew"
spicypete
This is a valid point -- I replied to why this is the way it is here: https://news.ycombinator.com/item?id=43629049. I understand it's not ideal for many, and I am open to PRs!
yjftsjthsd-h
> The RUN parser currently doesn't support grouping or semicolons in commands
But then example show that it does support `&&`? Why the difference? I pretty much always write
RUN foo && \
bar && \
:
but it seems syntactically identical to the also valid RUN set -e && \
foo ; \
bar ; \
:
mcstafford
I prefer heredoc[1] syntax.
I find it more readable and portable.
[1] https://www.docker.com/blog/introduction-to-heredocs-in-dock...
yjftsjthsd-h
Meta: In HN, prefix a line with 2 spaces to get code formatting, ex.
# syntax=docker/dockerfile:1.3-labs
FROM alpine
RUN <<EOF
echo "This is a heredoc example"
echo "It allows multi-line commands"
EOF
Non-meta: Do you happen to know how portable that is across old docker, podman/buildah, kaniko, etc.? I'd like to adopt it but I don't want it to bite me when I'm not running a recent version of literal docker.vbezhenar
It is new feature and not portable to old versions. But modern podman supports it. No idea about kaniko.
solatic
Or, you can write an actual shell script file (i.e. with a .sh extension) to be stored in your repository, ADD it in a throwaway context (i.e. multi-stage builds), then RUN --mount=type=bind to put it into a temporary directory in the build container so that you can execute it. This way, the script doesn't pollute the container, and you have proper separation of concerns, including the ability to use library functions, running shell linters directly, or using higher-level languages like Python if you really need it for some reason
xenophonf
That's bad practice because it hides build steps from `docker inspect`. Per https://github.com/docker-library/official-images#clarity:
> Try to make the Dockerfile easy to understand/read. It may be tempting, for the sake of brevity, to put complicated initialization details into a standalone script and merely add a RUN command in the Dockerfile. However, this causes the resulting Dockerfile to be overly opaque, and such Dockerfiles are unlikely to pass review. Instead, it is recommended to put all the commands for initialization into the Dockerfile as appropriate RUN or ENV command combinations. To find good examples, look at the current official images.
solatic
That's advice specifically for official images, and it dates back before multi-stage builds. Most people are not building official images. Most people benefit from clear encapsulation and separation of concerns. The Dockerfile sets up the environment to run the provisioning script, and a provisioning script does the actual provisioning. Official images are different because usually the provisioning script is hidden in an OS package installed with e.g. apk add (or are we going to pretend that OS packages are bad practice for the same reason?).
yjftsjthsd-h
Don't multi-stage builds already break `docker inspect`?
spicypete
I use `mvdan/sh` [1] under the hood for processing the commands. So it will reformat
if [ foo ] ; then
bar
fi
to if [ foo ]
then
bar
fi
And also format your example to foo
bar
In this type of situation, it becomes a little trickier to disambiguate when I need to add semicolons and a backslash, and when I need to add only backslashes. If you use `&&` -- you have disambiguated the two cases so I can format it.yjftsjthsd-h
Between that and the difficulty with comments, it feels like maybe not an ideal tool for the job? Although, I can't throw stones; I'd do almost anything to avoid having to write my own parser. (And not meant as an attack regardless, just trying to constructively question this particular design point)
spicypete
I am certainly not in the business of writing my own shell parser ;) Though this is a fair point -- I think I could get a more rich level of control over the output by hooking into their parser, albeit with a higher level of complexity.
null
urxvtcd
I'd love to indent the body of each stage in multi-stage dockerfiles, like:
FROM foo
...
FROM bar
...
It's easy to see at glance what's going on.xyst
This reminds me of SQL indentation “best practices” discussion [1]
I personally don’t find this particularly helpful but can see it helping some folks. You write enough dockerfiles, the formatting becomes irrelevant.
What pisses me off though is _inconsistency_. One code base uses "formatting practices 1b", then another code base uses "formatting practices 2x". Then the worst offender: a service owners that can’t make up their mind and each developer makes up their own "best practices".
[1] https://stackoverflow.com/questions/272210/sql-statement-ind...
xyst
It’s wild that this has to be a third party offering.
I wish projects would adopt/create their own formatters like go and rust.
The amount of time I have wasted discussing "best practices" with "senior" engineers is way too damn high.
In code bases such as go or rust, the discussion ends with the "the built in formatter and preferences is preferred"
nikeee
How does it handle multi-stage Dockerfiles? I always indent the steps following FROM to make the stages more obvious. I don't get why that isn't a norm because not doing it seems like not indenting function bodies in other languages.
QuarterDuplex
OK but is there any way to layer docker files? I'm not talking about compose, I mean like combining layers from different dockers in a nice way?
brigandish
That sounds like multi-stage builds https://docs.docker.com/build/building/multi-stage/
QuarterDuplex
or more like combining steps without explicitly having to combine layers?
travisgriggs
I hope there’s a config file for dockerfmt. Over time, it will get more and more options. It will approach Turing completeness.
Then we’ll need a formatter formatter.
Software is like onions said Shrek to Donkey.
righthand
I just use a yaml LSP which will probably try to lookup the schema.org Containerfile format for this. I first noticed this recently when working on a Github Actions yaml file. Pretty nifty.
whalesalad
But a dockerfile is not yaml. do you mean for docker compose?
righthand
You’re right. I’m sure there is a containerfile lsp however.
sandeepgogarla
[flagged]
I had a chuckle when I looked at the source code and could not find a Dockerfile in there. I want to kick the tires on it and the easiest way would be to run it as a Docker container against an existing file and alas, I cannot.