Skip to content

Commit

Permalink
Merge pull request #1693 from tonistiigi/dockerfile-comments
Browse files Browse the repository at this point in the history
dockerfile: parse comments associated with args and stages
  • Loading branch information
tiborvass authored Oct 13, 2020
2 parents 4177e7c + 0a7f6cc commit 7bdb659
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 18 deletions.
6 changes: 4 additions & 2 deletions frontend/dockerfile/instructions/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ func (kvp *KeyValuePair) String() string {

// KeyValuePairOptional is the same as KeyValuePair but Value is optional
type KeyValuePairOptional struct {
Key string
Value *string
Key string
Value *string
Comment string
}

func (kvpo *KeyValuePairOptional) ValueString() string {
Expand Down Expand Up @@ -419,6 +420,7 @@ type Stage struct {
SourceCode string
Platform string
Location []parser.Range
Comment string
}

// AddCommand to the stage
Expand Down
16 changes: 16 additions & 0 deletions frontend/dockerfile/instructions/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type parseRequest struct {
flags *BFlags
original string
location []parser.Range
comments []string
}

var parseRunPreHooks []func(*RunCommand, parseRequest) error
Expand Down Expand Up @@ -50,6 +51,7 @@ func newParseRequestFromNode(node *parser.Node) parseRequest {
original: node.Original,
flags: NewBFlagsWithArgs(node.Flags),
location: node.Location(),
comments: node.PrevComment,
}
}

Expand Down Expand Up @@ -289,6 +291,7 @@ func parseFrom(req parseRequest) (*Stage, error) {
Commands: []Command{},
Platform: flPlatform.Value,
Location: req.location,
Comment: getComment(req.comments, stageName),
}, nil

}
Expand Down Expand Up @@ -604,6 +607,7 @@ func parseArg(req parseRequest) (*ArgCommand, error) {
} else {
kvpo.Key = arg
}
kvpo.Comment = getComment(req.comments, kvpo.Key)
pairs[i] = kvpo
}

Expand Down Expand Up @@ -654,3 +658,15 @@ func errBlankCommandNames(command string) error {
func errTooManyArguments(command string) error {
return errors.Errorf("Bad input to %s, too many arguments", command)
}

func getComment(comments []string, name string) string {
if name == "" {
return ""
}
for _, line := range comments {
if strings.HasPrefix(line, name+" ") {
return strings.TrimPrefix(line, name+" ")
}
}
return ""
}
34 changes: 34 additions & 0 deletions frontend/dockerfile/instructions/parse_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package instructions

import (
"bytes"
"strings"
"testing"

Expand Down Expand Up @@ -140,6 +141,39 @@ func TestParseOptInterval(t *testing.T) {
require.NoError(t, err)
}

func TestCommentsDetection(t *testing.T) {
dt := `# foo sets foo
ARG foo=bar
# base defines first stage
FROM busybox AS base
# this is irrelevant
ARG foo
# bar defines bar
# baz is something else
ARG bar baz=123
`

ast, err := parser.Parse(bytes.NewBuffer([]byte(dt)))
require.NoError(t, err)

stages, meta, err := Parse(ast.AST)
require.NoError(t, err)

require.Equal(t, "defines first stage", stages[0].Comment)
require.Equal(t, "foo", meta[0].Args[0].Key)
require.Equal(t, "sets foo", meta[0].Args[0].Comment)

st := stages[0]

require.Equal(t, "foo", st.Commands[0].(*ArgCommand).Args[0].Key)
require.Equal(t, "", st.Commands[0].(*ArgCommand).Args[0].Comment)
require.Equal(t, "bar", st.Commands[1].(*ArgCommand).Args[0].Key)
require.Equal(t, "defines bar", st.Commands[1].(*ArgCommand).Args[0].Comment)
require.Equal(t, "baz", st.Commands[1].(*ArgCommand).Args[1].Key)
require.Equal(t, "is something else", st.Commands[1].(*ArgCommand).Args[1].Comment)
}

func TestErrorCases(t *testing.T) {
cases := []struct {
name string
Expand Down
2 changes: 1 addition & 1 deletion frontend/dockerfile/parser/line_parsers.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func parseSubCommand(rest string, d *directives) (*Node, map[string]bool, error)
return nil, nil, nil
}

child, err := newNodeFromLine(rest, d)
child, err := newNodeFromLine(rest, d, nil)
if err != nil {
return nil, nil, err
}
Expand Down
42 changes: 27 additions & 15 deletions frontend/dockerfile/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ import (
// works a little more effectively than a "proper" parse tree for our needs.
//
type Node struct {
Value string // actual content
Next *Node // the next item in the current sexp
Children []*Node // the children of this sexp
Attributes map[string]bool // special attributes for this node
Original string // original line used before parsing
Flags []string // only top Node should have this set
StartLine int // the line in the original dockerfile where the node begins
EndLine int // the line in the original dockerfile where the node ends
Value string // actual content
Next *Node // the next item in the current sexp
Children []*Node // the children of this sexp
Attributes map[string]bool // special attributes for this node
Original string // original line used before parsing
Flags []string // only top Node should have this set
StartLine int // the line in the original dockerfile where the node begins
EndLine int // the line in the original dockerfile where the node ends
PrevComment []string
}

// Location return the location of node in source code
Expand Down Expand Up @@ -191,7 +192,7 @@ func init() {
// newNodeFromLine splits the line into parts, and dispatches to a function
// based on the command and command arguments. A Node is created from the
// result of the dispatch.
func newNodeFromLine(line string, d *directives) (*Node, error) {
func newNodeFromLine(line string, d *directives, comments []string) (*Node, error) {
cmd, flags, args, err := splitCommand(line)
if err != nil {
return nil, err
Expand All @@ -208,11 +209,12 @@ func newNodeFromLine(line string, d *directives) (*Node, error) {
}

return &Node{
Value: cmd,
Original: line,
Flags: flags,
Next: next,
Attributes: attrs,
Value: cmd,
Original: line,
Flags: flags,
Next: next,
Attributes: attrs,
PrevComment: comments,
}, nil
}

Expand All @@ -239,6 +241,7 @@ func Parse(rwc io.Reader) (*Result, error) {
root := &Node{StartLine: -1}
scanner := bufio.NewScanner(rwc)
warnings := []string{}
var comments []string

var err error
for scanner.Scan() {
Expand All @@ -247,6 +250,14 @@ func Parse(rwc io.Reader) (*Result, error) {
// First line, strip the byte-order-marker if present
bytesRead = bytes.TrimPrefix(bytesRead, utf8bom)
}
if isComment(bytesRead) {
comment := strings.TrimSpace(string(bytesRead[1:]))
if comment == "" {
comments = nil
} else {
comments = append(comments, comment)
}
}
bytesRead, err = processLine(d, bytesRead, true)
if err != nil {
return nil, withLocation(err, currentLine, 0)
Expand Down Expand Up @@ -285,10 +296,11 @@ func Parse(rwc io.Reader) (*Result, error) {
warnings = append(warnings, "[WARNING]: Empty continuation line found in:\n "+line)
}

child, err := newNodeFromLine(line, d)
child, err := newNodeFromLine(line, d, comments)
if err != nil {
return nil, withLocation(err, startLine, currentLine)
}
comments = nil
root.AddChild(child, startLine, currentLine)
}

Expand Down

0 comments on commit 7bdb659

Please sign in to comment.