Repository navigation
Description
Problem
dockerfmt removes empty continuation lines — lines containing only a backslash (\) — that are used as visual separators between logical groups inside long RUN instructions:
FROM alpine
RUN \
--mount=type=bind,source=./a,target=/b \
\
echo one \
\
&& echo two \
\
&& echo threeis formatted to:
FROM alpine
RUN --mount=type=bind,source=./a,target=/b \
echo one \
&& echo two \
&& echo threeAll three separator lines are gone.
These lines are valid Dockerfile syntax (a continuation line with empty content). No linter complains about them: BuildKit's NoEmptyContinuation build check only fires on completely blank lines inside a continuation, and hadolint has no rule for them. So removing them is a pure formatting-style loss, and users currently have no way to keep them short of # dockerfmt-ignore on the whole directive.
Root cause
formatRun hands the RUN body to mvdan/sh (formatBash → syntax.Printer), and the mvdan/sh printer only preserves empty continuation lines in some positions. Probes with plain shfmt -bn:
Position of the \-only line |
Result |
|---|---|
| between two plain words | preserved |
before a line starting with && |
dropped |
| at the start of the content | dropped |
The third case is what bites after extractDirectiveContent splits the --mount flags off the body: any separator that followed the flags becomes a leading empty continuation and is dropped. Separators before && lines are dropped by the printer as well.
Suggested fix
dockerfmt already has a placeholder mechanism that lets comments survive the shfmt pass (preprocessShellComments / postprocessShellComments wrap comment lines in backtick placeholders and unwrap them afterwards). The same trick could be applied to \-only lines: temporarily replace each empty continuation line with a placeholder comment line (which the printer preserves in all positions), then unwrap it back into a bare \ after printing. That would preserve the separators everywhere, including before && lines and right after the flags.
Related: comments in the flags region are dropped
A manual workaround for the above is to use comment lines as separators. In the command region this works well and round-trips:
RUN \
--mount=type=bind,source=./a,target=/b \
# --- section
echo one \
# --- next
&& echo twoHowever, comments placed before or between RUN flags are silently removed, together with the leading RUN \ continuation:
FROM alpine
RUN \
# \
--mount=type=bind,source=./a,target=/b \
--mount=type=cache,target=/c \
echo one→ (single pass):
FROM alpine
RUN --mount=type=bind,source=./a,target=/b \
--mount=type=cache,target=/c \
echo oneThe same happens with a comment placed between two --mount flags. Since comments in the command region are preserved (and re-indented — a great feature), it would be consistent to preserve them in the flags region too.
Activity