fix(core): teach the Dockerfile parser about heredocs#17
Open
PunGrumpy wants to merge 3 commits into
Open
Conversation
The permissive case-insensitive [A-Z]+ regex treated any "word arg" line as an instruction, causing false parses. Anchor to the real Dockerfile keyword set (module-level, built once) while still accepting lowercase instructions.
BuildKit heredocs (<<EOF ... EOF) had no parser support: every body line was mis-parsed as its own instruction, producing phantom instructions (false positives, e.g. USER inside a RUN heredoc flipping no-root-user) and losing the real RUN command (false negatives for content-based rules like clean-package-cache and combine-apt-update-install). Add heredoc state tracking to the Dockerfile parser: detect one or more openers per line (<<EOF, <<-EOF, <<'EOF', <<"EOF"), fold body lines into the owning instruction's args (not matched as instructions, comments included verbatim), and close in FIFO delimiter order. An opener implies continuation even without a trailing backslash; an unterminated heredoc still emits at EOF rather than being dropped. Add an end-to-end heredoc fixture and CLI test proving the fix reaches content-based rules (clean-package-cache now fires on the heredoc RUN body).
packages/core is bundled into @docker-doctor/cli, and heredoc bodies now feed content-based rules, so this is a published behavior change.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Jul 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Teaches the Dockerfile parser about heredoc (
<<EOF) syntax and anchors the instruction regex to the real Dockerfile keyword set. Implements plan 005. Depends on 002 (CLI integration tests) and 003 (rule-specificity fixes).Why
The parser had no heredoc handling and a case-insensitive
[A-Z]+instruction match, so every heredoc body line was parsed as an instruction:RUN <<EOF apt-get update apt-get install -y curl EOF...parsed to a
RUNwhose args were literally<<EOF(the real command lost) plus phantomAPT-GETinstructions. Two consequences, both fixed here and verified live against the built CLI:clean-package-cache,combine-apt-update-install,use-pipefail,avoid-dev-dependencies,order-layers) inspectRUNargs and saw only<<EOF. Now the body is folded into the instruction's args, so a heredocRUNlooks the same to a rule as a backslash-continuationRUN. Confirmed:clean-package-cache+combine-apt-update-installnow fire on a heredocapt-get install.USER rootline inside an unrelated heredoc body flippedno-root-user. Confirmed: phantomUSER rootin a heredoc body no longer produces a diagnostic.How
[A-Za-z]+extracts a candidate word, then a module-levelDOCKERFILE_KEYWORDSSet gates it — a lowercasefrom node:22is still valid, butapt-get updateis no longer an "instruction".COPY <<F1 <<F2 /dest/), closed in open order; a heredoc opener implies continuation without a trailing\; the#-comment skip is guarded so shell comments inside a body aren't dropped; EOF flushes an unterminated heredoc instead of crashing.The parser was refactored into small pure helpers (
matchInstructionKeyword,findHeredocDelimiters,processHeredocLine,processInstructionLine) to satisfy the repo'seslint(complexity)gate — control flow and semantics are unchanged (all pre-existing parser and rule tests pass unchanged).Deliberately not done (design decision): no
heredocfield onDockerfileInstruction— folding the body intoargsis what makes every content rule work without touching any of them. Known remaining gaps documented in the plan:# escape=directive,# syntax=frontmatter,ARGbefore firstFROM.Behavior change (why the changeset)
.changeset/dockerfile-heredoc-parsing.md→@docker-doctor/clipatch. Scan output changes on real Dockerfiles that use heredocs (previously-hidden violations now surface; phantom-instruction diagnostics disappear).Tests
packages/core/test/parser.test.ts— +7 tests (keyword anchoring, lowercase, heredoc body-into-args, body-not-instructions, quoted/<<-delimiters,rawpreservation, CRLF). Purely additive.packages/docker-doctor/test/fixtures/heredoc/Dockerfile+ a CLI test asserting ≥1 diagnostic — end-to-end proof the parser fix reaches the rule layer.ultracite check0.Stacking
Base is
advisor/003-fix-rule-specificity-bugs(PR #15), which stacks onadvisor/002-...(PR #14). This is a sibling of PR #16 (004) — both branch off 003, and 005 does not touch the image-ref parser 004 rewrote, so there's no conflict. GitHub auto-retargets this PR tomainonce #14 and #15 merge. Merge order: #14 → #15 → (#16 and this, in either order).🤖 Generated with Claude Code