Skip to content

Commit 51daeb3

Browse files
authored
Modify jsonnet-lint to accept multiple input files (#545)
Modify jsonnet-lint to accept multiple input files
1 parent 46d1fce commit 51daeb3

File tree

3 files changed

+106
-53
lines changed

3 files changed

+106
-53
lines changed

cmd/jsonnet-lint/cmd.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func version(o io.Writer) {
2121
func usage(o io.Writer) {
2222
version(o)
2323
fmt.Fprintln(o)
24-
fmt.Fprintln(o, "jsonnet-lint {<option>} { <filename> }")
24+
fmt.Fprintln(o, "jsonnet-lint {<option>} { <filenames ...> }")
2525
fmt.Fprintln(o)
2626
fmt.Fprintln(o, "Available options:")
2727
fmt.Fprintln(o, " -h / --help This message")
@@ -53,8 +53,8 @@ func usage(o io.Writer) {
5353

5454
type config struct {
5555
// TODO(sbarzowski) Allow multiple root files checked at once for greater efficiency
56-
inputFile string
57-
evalJpath []string
56+
inputFiles []string
57+
evalJpath []string
5858
}
5959

6060
func makeConfig() config {
@@ -112,11 +112,7 @@ func processArgs(givenArgs []string, config *config, vm *jsonnet.VM) (processArg
112112
return processArgsStatusFailureUsage, fmt.Errorf("file not provided")
113113
}
114114

115-
if len(remainingArgs) > 1 {
116-
return processArgsStatusFailure, fmt.Errorf("only one file is allowed")
117-
}
118-
119-
config.inputFile = remainingArgs[0]
115+
config.inputFiles = remainingArgs
120116
return processArgsStatusContinue, nil
121117
}
122118

@@ -164,27 +160,27 @@ func main() {
164160
JPaths: config.evalJpath,
165161
})
166162

167-
inputFile, err := os.Open(config.inputFile)
168-
if err != nil {
169-
die(err)
170-
}
171-
data, err := ioutil.ReadAll(inputFile)
172-
if err != nil {
173-
die(err)
174-
}
175-
err = inputFile.Close()
176-
if err != nil {
177-
die(err)
163+
var snippets []linter.Snippet
164+
for _, inputFile := range config.inputFiles {
165+
f, err := os.Open(inputFile)
166+
if err != nil {
167+
die(err)
168+
}
169+
data, err := ioutil.ReadAll(f)
170+
if err != nil {
171+
die(err)
172+
}
173+
err = f.Close()
174+
if err != nil {
175+
die(err)
176+
}
177+
178+
snippets = append(snippets, linter.Snippet{FileName: inputFile, Code: string(data)})
178179
}
179180

180181
cmd.MemProfile()
181182

182-
_, err = jsonnet.SnippetToAST(config.inputFile, string(data))
183-
if err != nil {
184-
die(err)
185-
}
186-
187-
errorsFound := linter.LintSnippet(vm, os.Stderr, config.inputFile, string(data))
183+
errorsFound := linter.LintSnippet(vm, os.Stderr, snippets)
188184
if errorsFound {
189185
fmt.Fprintf(os.Stderr, "Problems found!\n")
190186
os.Exit(2)

linter/linter.go

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ type ErrorWriter struct {
2222
Writer io.Writer
2323
}
2424

25+
// Snippet represents a jsonnet file data that to be linted
26+
type Snippet struct {
27+
FileName string
28+
Code string
29+
}
30+
2531
func (e *ErrorWriter) writeError(vm *jsonnet.VM, err errors.StaticError) {
2632
e.ErrorsFound = true
2733
_, writeErr := e.Writer.Write([]byte(vm.ErrorFormatter.Format(err) + "\n"))
@@ -38,10 +44,14 @@ type nodeWithLocation struct {
3844
}
3945

4046
// Lint analyses a node and reports any issues it encounters to an error writer.
41-
func lint(vm *jsonnet.VM, node nodeWithLocation, errWriter *ErrorWriter) {
47+
func lint(vm *jsonnet.VM, nodes []nodeWithLocation, errWriter *ErrorWriter) {
4248
roots := make(map[string]ast.Node)
43-
roots[node.path] = node.node
44-
getImports(vm, node, roots, errWriter)
49+
for _, node := range nodes {
50+
roots[node.path] = node.node
51+
}
52+
for _, node := range nodes {
53+
getImports(vm, node, roots, errWriter)
54+
}
4555

4656
variablesInFile := make(map[string]common.VariableInfo)
4757

@@ -55,35 +65,38 @@ func lint(vm *jsonnet.VM, node nodeWithLocation, errWriter *ErrorWriter) {
5565
return variables.FindVariables(node.node, variables.Environment{"std": &std})
5666
}
5767

58-
variableInfo := findVariables(node)
5968
for importedPath, rootNode := range roots {
6069
variablesInFile[importedPath] = *findVariables(nodeWithLocation{rootNode, importedPath})
6170
}
6271

63-
for _, v := range variableInfo.Variables {
64-
if len(v.Occurences) == 0 && v.VariableKind == common.VarRegular && v.Name != "$" {
65-
errWriter.writeError(vm, errors.MakeStaticError("Unused variable: "+string(v.Name), v.LocRange))
66-
}
67-
}
68-
ec := common.ErrCollector{}
69-
7072
vars := make(map[string]map[ast.Node]*common.Variable)
7173
for importedPath, info := range variablesInFile {
7274
vars[importedPath] = info.VarAt
7375
}
7476

75-
types.Check(node.node, roots, vars, func(currentPath, importedPath string) ast.Node {
76-
node, _, err := vm.ImportAST(currentPath, importedPath)
77-
if err != nil {
78-
return nil
77+
for _, node := range nodes {
78+
variableInfo := findVariables(node)
79+
80+
for _, v := range variableInfo.Variables {
81+
if len(v.Occurences) == 0 && v.VariableKind == common.VarRegular && v.Name != "$" {
82+
errWriter.writeError(vm, errors.MakeStaticError("Unused variable: "+string(v.Name), v.LocRange))
83+
}
7984
}
80-
return node
81-
}, &ec)
85+
ec := common.ErrCollector{}
86+
87+
types.Check(node.node, roots, vars, func(currentPath, importedPath string) ast.Node {
88+
node, _, err := vm.ImportAST(currentPath, importedPath)
89+
if err != nil {
90+
return nil
91+
}
92+
return node
93+
}, &ec)
8294

83-
traversal.Traverse(node.node, &ec)
95+
traversal.Traverse(node.node, &ec)
8496

85-
for _, err := range ec.Errs {
86-
errWriter.writeError(vm, err)
97+
for _, err := range ec.Errs {
98+
errWriter.writeError(vm, err)
99+
}
87100
}
88101
}
89102

@@ -118,18 +131,24 @@ func getImports(vm *jsonnet.VM, node nodeWithLocation, roots map[string]ast.Node
118131
}
119132
}
120133

121-
// LintSnippet checks for problems in a single code snippet.
122-
func LintSnippet(vm *jsonnet.VM, output io.Writer, filename, code string) bool {
134+
// LintSnippet checks for problems in code snippet(s).
135+
func LintSnippet(vm *jsonnet.VM, output io.Writer, snippets []Snippet) bool {
123136
errWriter := ErrorWriter{
124137
Writer: output,
125138
ErrorsFound: false,
126139
}
127140

128-
node, err := jsonnet.SnippetToAST(filename, code)
129-
if err != nil {
130-
errWriter.writeError(vm, err.(errors.StaticError)) // ugly but true
131-
return true
141+
var nodes []nodeWithLocation
142+
for _, snippet := range snippets {
143+
node, err := jsonnet.SnippetToAST(snippet.FileName, snippet.Code)
144+
145+
if err != nil {
146+
errWriter.writeError(vm, err.(errors.StaticError)) // ugly but true
147+
} else {
148+
nodes = append(nodes, nodeWithLocation{node, snippet.FileName})
149+
}
132150
}
133-
lint(vm, nodeWithLocation{node, filename}, &errWriter)
151+
152+
lint(vm, nodes, &errWriter)
134153
return errWriter.ErrorsFound
135154
}

linter/linter_test.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func runTest(t *testing.T, test *linterTest, changedGoldensList *ChangedGoldensL
4040

4141
var outBuilder strings.Builder
4242

43-
errorsFound := LintSnippet(vm, &outBuilder, test.name, string(input))
43+
errorsFound := LintSnippet(vm, &outBuilder, []Snippet{Snippet{FileName: test.name, Code: string(input)}})
4444

4545
outData := outBuilder.String()
4646

@@ -72,6 +72,40 @@ func runTest(t *testing.T, test *linterTest, changedGoldensList *ChangedGoldensL
7272
}
7373
}
7474

75+
func runTests(t *testing.T, tests []*linterTest) {
76+
read := func(file string) []byte {
77+
bytz, err := ioutil.ReadFile(file)
78+
if err != nil {
79+
t.Fatalf("reading file: %s: %v", file, err)
80+
}
81+
return bytz
82+
}
83+
84+
vm := jsonnet.MakeVM()
85+
86+
var snippets []Snippet
87+
88+
for _, test := range tests {
89+
input := read(test.input)
90+
91+
snippets = append(snippets, Snippet{FileName: test.name, Code: string(input)})
92+
}
93+
94+
var outBuilder strings.Builder
95+
96+
errorsFound := LintSnippet(vm, &outBuilder, snippets)
97+
98+
outData := outBuilder.String()
99+
100+
if outData == "" && errorsFound {
101+
t.Error(fmt.Errorf("return value indicates problems present, but no output was produced"))
102+
}
103+
104+
if outData != "" && !errorsFound {
105+
t.Error(fmt.Errorf("return value indicates no problems, but output is not empty:\n%v", outData))
106+
}
107+
}
108+
75109
func TestLinter(t *testing.T) {
76110
flag.Parse()
77111

@@ -122,4 +156,8 @@ func TestLinter(t *testing.T) {
122156
t.Fail()
123157
})
124158
}
159+
160+
t.Run("passing multiple input files", func(t *testing.T) {
161+
runTests(t, tests)
162+
})
125163
}

0 commit comments

Comments
 (0)