From c33a0ef44072c56c6f94ce2cd03c587775cf7ba4 Mon Sep 17 00:00:00 2001 From: Sebastian Echeverria Date: Fri, 18 Oct 2024 12:56:17 -0400 Subject: [PATCH] add support for heredocs --- go.mod | 4 +-- parse.go | 36 ++++++++++++++++++----- parse_test.go | 60 ++++++++++++++++++++++++++++++++++++++ pylib/main.go | 50 +++++++++++++++++++++++++++++-- pylib/support.c | 49 ++++++++++++++++++++++++++----- tests/dockerfile_test.py | 63 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 242 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 04d59b8..2c5f860 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/asottile/dockerfile go 1.16 require ( - github.com/moby/buildkit v0.9.0 - github.com/stretchr/testify v1.7.0 + github.com/moby/buildkit v0.12.5 + github.com/stretchr/testify v1.8.3 ) diff --git a/parse.go b/parse.go index 73107ea..e49136b 100644 --- a/parse.go +++ b/parse.go @@ -9,17 +9,25 @@ import ( "github.com/moby/buildkit/frontend/dockerfile/parser" ) +// Represents info about a heredoc. +type Heredoc struct { + Name string + FileDescriptor uint + Content string +} + // Represents a single line (layer) in a Dockerfile. // For example `FROM ubuntu:xenial` type Command struct { - Cmd string // lowercased command name (ex: `from`) - SubCmd string // for ONBUILD only this holds the sub-command - Json bool // whether the value is written in json form - Original string // The original source line - StartLine int // The original source line number which starts this command - EndLine int // The original source line number which ends this command - Flags []string // Any flags such as `--from=...` for `COPY`. - Value []string // The contents of the command (ex: `ubuntu:xenial`) + Cmd string // lowercased command name (ex: `from`) + SubCmd string // for ONBUILD only this holds the sub-command + Json bool // whether the value is written in json form + Original string // The original source line + StartLine int // The original source line number which starts this command + EndLine int // The original source line number which ends this command + Flags []string // Any flags such as `--from=...` for `COPY`. + Value []string // The contents of the command (ex: `ubuntu:xenial`) + Heredocs []Heredoc // Extra heredoc content attachments } // A failure in opening a file for reading. @@ -78,6 +86,18 @@ func ParseReader(file io.Reader) ([]Command, error) { cmd.Value = append(cmd.Value, n.Value) } + if len(child.Heredocs) != 0 { + // For heredocs, add heredocs extra lines to Original, + // and to the heredocs list. + cmd.Original = cmd.Original + "\n" + for _, heredoc := range child.Heredocs { + cmd.Original = cmd.Original + heredoc.Content + heredoc.Name + "\n" + cmd.Heredocs = append(cmd.Heredocs, Heredoc{Name: heredoc.Name, + FileDescriptor: heredoc.FileDescriptor, + Content: heredoc.Content}) + } + } + ret = append(ret, cmd) } return ret, nil diff --git a/parse_test.go b/parse_test.go index bf3b18f..84c85b5 100644 --- a/parse_test.go +++ b/parse_test.go @@ -116,3 +116,63 @@ func TestParseFile(t *testing.T) { } assert.Equal(t, expected, cmds) } + +func TestParseReaderHeredocs(t *testing.T) { + dockerfile := `RUN 3<> /hello +echo "World!" >> /hello +EOF +` + cmds, err := ParseReader(bytes.NewBufferString(dockerfile)) + assert.Nil(t, err) + expected := []Command{ + Command{ + Cmd: "RUN", + Original: dockerfile, + StartLine: 1, + EndLine: 5, + Flags: []string{}, + Value: []string{"3<> /hello\necho \"World!\" >> /hello\n"}, + }, + }, + } + assert.Equal(t, expected, cmds) +} + +func TestParseReaderHeredocsMultiple(t *testing.T) { + dockerfile := `COPY <> /hello\n' + 'echo "World!" >> /hello\n' + 'EOF\n' + ) + ret = dockerfile.parse_string(test_string) + assert ret == ( + dockerfile.Command( + cmd='RUN', sub_cmd=None, json=False, flags=(), + value=( + '3<> /hello\n' + 'echo "World!" >> /hello\n', + file_descriptor=3, + ), + ), + ), + ) + + +def test_heredoc_string_multiple_success(): + test_string = ( + 'COPY <