@@ -30,7 +30,7 @@ func Parse(_ context.Context, r io.Reader, path string) (any, error) {
3030
3131 instr , err := instructions .ParseInstruction (child )
3232 if err != nil {
33- return nil , fmt .Errorf ("process dockerfile instructions : %w" , err )
33+ return nil , fmt .Errorf ("parse dockerfile instruction : %w" , err )
3434 }
3535
3636 if _ , ok := instr .(* instructions.Stage ); ok {
@@ -56,14 +56,27 @@ func Parse(_ context.Context, r io.Reader, path string) (any, error) {
5656 EndLine : child .EndLine ,
5757 }
5858
59+ // processing statement with sub-statement
60+ // example: ONBUILD RUN foo bar
61+ // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#onbuild
5962 if child .Next != nil && len (child .Next .Children ) > 0 {
6063 cmd .SubCmd = child .Next .Children [0 ].Value
6164 child = child .Next .Children [0 ]
6265 }
6366
67+ // mark if the instruction is in exec form
68+ // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#exec-form
6469 cmd .JSON = child .Attributes ["json" ]
65- for n := child .Next ; n != nil ; n = n .Next {
66- cmd .Value = append (cmd .Value , n .Value )
70+
71+ // heredoc may contain a script that will be executed in the shell, so we need to process it
72+ // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#here-documents
73+ if len (child .Heredocs ) > 0 && child .Next != nil {
74+ cmd .Original = originalFromHeredoc (child )
75+ cmd .Value = []string {processHeredoc (child )}
76+ } else {
77+ for n := child .Next ; n != nil ; n = n .Next {
78+ cmd .Value = append (cmd .Value , n .Value )
79+ }
6780 }
6881
6982 stage .Commands = append (stage .Commands , cmd )
@@ -75,3 +88,44 @@ func Parse(_ context.Context, r io.Reader, path string) (any, error) {
7588
7689 return & parsedFile , nil
7790}
91+
92+ func originalFromHeredoc (node * parser.Node ) string {
93+ var sb strings.Builder
94+ sb .WriteString (node .Original )
95+ sb .WriteRune ('\n' )
96+ for i , heredoc := range node .Heredocs {
97+ sb .WriteString (heredoc .Content )
98+ sb .WriteString (heredoc .Name )
99+ if i != len (node .Heredocs )- 1 {
100+ sb .WriteRune ('\n' )
101+ }
102+ }
103+
104+ return sb .String ()
105+ }
106+
107+ // heredoc processing taken from here
108+ // https://github.com/moby/buildkit/blob/9a39e2c112b7c98353c27e64602bc08f31fe356e/frontend/dockerfile/dockerfile2llb/convert.go#L1200
109+ func processHeredoc (node * parser.Node ) string {
110+ if parser .MustParseHeredoc (node .Next .Value ) == nil || strings .HasPrefix (node .Heredocs [0 ].Content , "#!" ) {
111+ // more complex heredoc is passed to the shell as is
112+ var sb strings.Builder
113+ sb .WriteString (node .Next .Value )
114+ for _ , heredoc := range node .Heredocs {
115+ sb .WriteRune ('\n' )
116+ sb .WriteString (heredoc .Content )
117+ sb .WriteString (heredoc .Name )
118+ }
119+ return sb .String ()
120+ }
121+
122+ // simple heredoc and the content is run in a shell
123+ content := node .Heredocs [0 ].Content
124+ if node .Heredocs [0 ].Chomp {
125+ content = parser .ChompHeredocContent (content )
126+ }
127+
128+ content = strings .ReplaceAll (content , "\r \n " , "\n " )
129+ cmds := strings .Split (strings .TrimSuffix (content , "\n " ), "\n " )
130+ return strings .Join (cmds , " ; " )
131+ }
0 commit comments