@@ -30,7 +30,7 @@ func Parse(_ context.Context, r io.Reader, path string) (any, error) {
30
30
31
31
instr , err := instructions .ParseInstruction (child )
32
32
if err != nil {
33
- return nil , fmt .Errorf ("process dockerfile instructions : %w" , err )
33
+ return nil , fmt .Errorf ("parse dockerfile instruction : %w" , err )
34
34
}
35
35
36
36
if _ , ok := instr .(* instructions.Stage ); ok {
@@ -56,14 +56,27 @@ func Parse(_ context.Context, r io.Reader, path string) (any, error) {
56
56
EndLine : child .EndLine ,
57
57
}
58
58
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
59
62
if child .Next != nil && len (child .Next .Children ) > 0 {
60
63
cmd .SubCmd = child .Next .Children [0 ].Value
61
64
child = child .Next .Children [0 ]
62
65
}
63
66
67
+ // mark if the instruction is in exec form
68
+ // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#exec-form
64
69
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
+ }
67
80
}
68
81
69
82
stage .Commands = append (stage .Commands , cmd )
@@ -75,3 +88,44 @@ func Parse(_ context.Context, r io.Reader, path string) (any, error) {
75
88
76
89
return & parsedFile , nil
77
90
}
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