Browse code

Add line information in the parsed Dockerfile

Signed-off-by: perhapszzy@sina.com <perhapszzy@sina.com>

perhapszzy@sina.com authored on 2015/11/02 06:28:30
Showing 3 changed files
... ...
@@ -30,6 +30,8 @@ type Node struct {
30 30
 	Attributes map[string]bool // special attributes for this node
31 31
 	Original   string          // original line used before parsing
32 32
 	Flags      []string        // only top Node should have this set
33
+	StartLine  int             // the line in the original dockerfile where the node begins
34
+	EndLine    int             // the line in the original dockerfile where the node ends
33 35
 }
34 36
 
35 37
 var (
... ...
@@ -101,19 +103,24 @@ func parseLine(line string) (string, *Node, error) {
101 101
 // Parse is the main parse routine.
102 102
 // It handles an io.ReadWriteCloser and returns the root of the AST.
103 103
 func Parse(rwc io.Reader) (*Node, error) {
104
+	currentLine := 0
104 105
 	root := &Node{}
106
+	root.StartLine = -1
105 107
 	scanner := bufio.NewScanner(rwc)
106 108
 
107 109
 	for scanner.Scan() {
108 110
 		scannedLine := strings.TrimLeftFunc(scanner.Text(), unicode.IsSpace)
111
+		currentLine++
109 112
 		line, child, err := parseLine(scannedLine)
110 113
 		if err != nil {
111 114
 			return nil, err
112 115
 		}
116
+		startLine := currentLine
113 117
 
114 118
 		if line != "" && child == nil {
115 119
 			for scanner.Scan() {
116 120
 				newline := scanner.Text()
121
+				currentLine++
117 122
 
118 123
 				if stripComments(strings.TrimSpace(newline)) == "" {
119 124
 					continue
... ...
@@ -137,6 +144,15 @@ func Parse(rwc io.Reader) (*Node, error) {
137 137
 		}
138 138
 
139 139
 		if child != nil {
140
+			// Update the line information for the current child.
141
+			child.StartLine = startLine
142
+			child.EndLine = currentLine
143
+			// Update the line information for the root. The starting line of the root is always the
144
+			// starting line of the first child and the ending line is the ending line of the last child.
145
+			if root.StartLine < 0 {
146
+				root.StartLine = currentLine
147
+			}
148
+			root.EndLine = currentLine
140 149
 			root.Children = append(root.Children, child)
141 150
 		}
142 151
 	}
... ...
@@ -10,6 +10,7 @@ import (
10 10
 
11 11
 const testDir = "testfiles"
12 12
 const negativeTestDir = "testfiles-negative"
13
+const testFileLineInfo = "testfile-line/Dockerfile"
13 14
 
14 15
 func getDirs(t *testing.T, dir string) []string {
15 16
 	f, err := os.Open(dir)
... ...
@@ -110,3 +111,37 @@ func TestParseWords(t *testing.T) {
110 110
 		}
111 111
 	}
112 112
 }
113
+
114
+func TestLineInformation(t *testing.T) {
115
+	df, err := os.Open(testFileLineInfo)
116
+	if err != nil {
117
+		t.Fatalf("Dockerfile missing for %s: %v", testFileLineInfo, err)
118
+	}
119
+	defer df.Close()
120
+
121
+	ast, err := Parse(df)
122
+	if err != nil {
123
+		t.Fatalf("Error parsing dockerfile %s: %v", testFileLineInfo, err)
124
+	}
125
+
126
+	if ast.StartLine != 4 || ast.EndLine != 30 {
127
+		fmt.Fprintf(os.Stderr, "Wrong root line information: expected(%d-%d), actual(%d-%d)\n", 4, 30, ast.StartLine, ast.EndLine)
128
+		t.Fatalf("Root line information doesn't match result.")
129
+	}
130
+	if len(ast.Children) != 3 {
131
+		fmt.Fprintf(os.Stderr, "Wrong number of child: expected(%d), actual(%d)\n", 3, len(ast.Children))
132
+		t.Fatalf("Root line information doesn't match result.")
133
+	}
134
+	expected := [][]int{
135
+		{4, 4},
136
+		{10, 11},
137
+		{16, 30},
138
+	}
139
+	for i, child := range ast.Children {
140
+		if child.StartLine != expected[i][0] || child.EndLine != expected[i][1] {
141
+			fmt.Fprintf(os.Stderr, "Wrong line information for child %d: expected(%d-%d), actual(%d-%d)\n",
142
+				i, expected[i][0], expected[i][1], child.StartLine, child.EndLine)
143
+			t.Fatalf("Root line information doesn't match result.")
144
+		}
145
+	}
146
+}
113 147
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+
1
+
2
+
3
+FROM brimstone/ubuntu:14.04
4
+
5
+
6
+# TORUN -v /var/run/docker.sock:/var/run/docker.sock
7
+
8
+
9
+ENV GOPATH \
10
+/go
11
+
12
+
13
+
14
+# Install the packages we need, clean up after them and us
15
+RUN apt-get update \
16
+	&& dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean \
17
+
18
+
19
+    && apt-get install -y --no-install-recommends git golang ca-certificates \
20
+    && apt-get clean \
21
+    && rm -rf /var/lib/apt/lists \
22
+
23
+	&& go get -v github.com/brimstone/consuldock \
24
+    && mv $GOPATH/bin/consuldock /usr/local/bin/consuldock \
25
+
26
+	&& dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \
27
+	&& apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \
28
+	&& rm /tmp/dpkg.* \
29
+	&& rm -rf $GOPATH
30
+
31
+
32
+
33
+