mirror of
https://github.com/google/go-jsonnet.git
synced 2025-08-07 23:07:14 +02:00
Modify jsonnet-lint to accept multiple input files (#545)
Modify jsonnet-lint to accept multiple input files
This commit is contained in:
parent
46d1fceb9c
commit
51daeb3229
@ -21,7 +21,7 @@ func version(o io.Writer) {
|
|||||||
func usage(o io.Writer) {
|
func usage(o io.Writer) {
|
||||||
version(o)
|
version(o)
|
||||||
fmt.Fprintln(o)
|
fmt.Fprintln(o)
|
||||||
fmt.Fprintln(o, "jsonnet-lint {<option>} { <filename> }")
|
fmt.Fprintln(o, "jsonnet-lint {<option>} { <filenames ...> }")
|
||||||
fmt.Fprintln(o)
|
fmt.Fprintln(o)
|
||||||
fmt.Fprintln(o, "Available options:")
|
fmt.Fprintln(o, "Available options:")
|
||||||
fmt.Fprintln(o, " -h / --help This message")
|
fmt.Fprintln(o, " -h / --help This message")
|
||||||
@ -53,7 +53,7 @@ func usage(o io.Writer) {
|
|||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
// TODO(sbarzowski) Allow multiple root files checked at once for greater efficiency
|
// TODO(sbarzowski) Allow multiple root files checked at once for greater efficiency
|
||||||
inputFile string
|
inputFiles []string
|
||||||
evalJpath []string
|
evalJpath []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,11 +112,7 @@ func processArgs(givenArgs []string, config *config, vm *jsonnet.VM) (processArg
|
|||||||
return processArgsStatusFailureUsage, fmt.Errorf("file not provided")
|
return processArgsStatusFailureUsage, fmt.Errorf("file not provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(remainingArgs) > 1 {
|
config.inputFiles = remainingArgs
|
||||||
return processArgsStatusFailure, fmt.Errorf("only one file is allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
config.inputFile = remainingArgs[0]
|
|
||||||
return processArgsStatusContinue, nil
|
return processArgsStatusContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,27 +160,27 @@ func main() {
|
|||||||
JPaths: config.evalJpath,
|
JPaths: config.evalJpath,
|
||||||
})
|
})
|
||||||
|
|
||||||
inputFile, err := os.Open(config.inputFile)
|
var snippets []linter.Snippet
|
||||||
|
for _, inputFile := range config.inputFiles {
|
||||||
|
f, err := os.Open(inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(err)
|
die(err)
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadAll(inputFile)
|
data, err := ioutil.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(err)
|
die(err)
|
||||||
}
|
}
|
||||||
err = inputFile.Close()
|
err = f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(err)
|
die(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snippets = append(snippets, linter.Snippet{FileName: inputFile, Code: string(data)})
|
||||||
|
}
|
||||||
|
|
||||||
cmd.MemProfile()
|
cmd.MemProfile()
|
||||||
|
|
||||||
_, err = jsonnet.SnippetToAST(config.inputFile, string(data))
|
errorsFound := linter.LintSnippet(vm, os.Stderr, snippets)
|
||||||
if err != nil {
|
|
||||||
die(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
errorsFound := linter.LintSnippet(vm, os.Stderr, config.inputFile, string(data))
|
|
||||||
if errorsFound {
|
if errorsFound {
|
||||||
fmt.Fprintf(os.Stderr, "Problems found!\n")
|
fmt.Fprintf(os.Stderr, "Problems found!\n")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
@ -22,6 +22,12 @@ type ErrorWriter struct {
|
|||||||
Writer io.Writer
|
Writer io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snippet represents a jsonnet file data that to be linted
|
||||||
|
type Snippet struct {
|
||||||
|
FileName string
|
||||||
|
Code string
|
||||||
|
}
|
||||||
|
|
||||||
func (e *ErrorWriter) writeError(vm *jsonnet.VM, err errors.StaticError) {
|
func (e *ErrorWriter) writeError(vm *jsonnet.VM, err errors.StaticError) {
|
||||||
e.ErrorsFound = true
|
e.ErrorsFound = true
|
||||||
_, writeErr := e.Writer.Write([]byte(vm.ErrorFormatter.Format(err) + "\n"))
|
_, writeErr := e.Writer.Write([]byte(vm.ErrorFormatter.Format(err) + "\n"))
|
||||||
@ -38,10 +44,14 @@ type nodeWithLocation struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lint analyses a node and reports any issues it encounters to an error writer.
|
// Lint analyses a node and reports any issues it encounters to an error writer.
|
||||||
func lint(vm *jsonnet.VM, node nodeWithLocation, errWriter *ErrorWriter) {
|
func lint(vm *jsonnet.VM, nodes []nodeWithLocation, errWriter *ErrorWriter) {
|
||||||
roots := make(map[string]ast.Node)
|
roots := make(map[string]ast.Node)
|
||||||
|
for _, node := range nodes {
|
||||||
roots[node.path] = node.node
|
roots[node.path] = node.node
|
||||||
|
}
|
||||||
|
for _, node := range nodes {
|
||||||
getImports(vm, node, roots, errWriter)
|
getImports(vm, node, roots, errWriter)
|
||||||
|
}
|
||||||
|
|
||||||
variablesInFile := make(map[string]common.VariableInfo)
|
variablesInFile := make(map[string]common.VariableInfo)
|
||||||
|
|
||||||
@ -55,11 +65,18 @@ func lint(vm *jsonnet.VM, node nodeWithLocation, errWriter *ErrorWriter) {
|
|||||||
return variables.FindVariables(node.node, variables.Environment{"std": &std})
|
return variables.FindVariables(node.node, variables.Environment{"std": &std})
|
||||||
}
|
}
|
||||||
|
|
||||||
variableInfo := findVariables(node)
|
|
||||||
for importedPath, rootNode := range roots {
|
for importedPath, rootNode := range roots {
|
||||||
variablesInFile[importedPath] = *findVariables(nodeWithLocation{rootNode, importedPath})
|
variablesInFile[importedPath] = *findVariables(nodeWithLocation{rootNode, importedPath})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vars := make(map[string]map[ast.Node]*common.Variable)
|
||||||
|
for importedPath, info := range variablesInFile {
|
||||||
|
vars[importedPath] = info.VarAt
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
variableInfo := findVariables(node)
|
||||||
|
|
||||||
for _, v := range variableInfo.Variables {
|
for _, v := range variableInfo.Variables {
|
||||||
if len(v.Occurences) == 0 && v.VariableKind == common.VarRegular && v.Name != "$" {
|
if len(v.Occurences) == 0 && v.VariableKind == common.VarRegular && v.Name != "$" {
|
||||||
errWriter.writeError(vm, errors.MakeStaticError("Unused variable: "+string(v.Name), v.LocRange))
|
errWriter.writeError(vm, errors.MakeStaticError("Unused variable: "+string(v.Name), v.LocRange))
|
||||||
@ -67,11 +84,6 @@ func lint(vm *jsonnet.VM, node nodeWithLocation, errWriter *ErrorWriter) {
|
|||||||
}
|
}
|
||||||
ec := common.ErrCollector{}
|
ec := common.ErrCollector{}
|
||||||
|
|
||||||
vars := make(map[string]map[ast.Node]*common.Variable)
|
|
||||||
for importedPath, info := range variablesInFile {
|
|
||||||
vars[importedPath] = info.VarAt
|
|
||||||
}
|
|
||||||
|
|
||||||
types.Check(node.node, roots, vars, func(currentPath, importedPath string) ast.Node {
|
types.Check(node.node, roots, vars, func(currentPath, importedPath string) ast.Node {
|
||||||
node, _, err := vm.ImportAST(currentPath, importedPath)
|
node, _, err := vm.ImportAST(currentPath, importedPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -85,6 +97,7 @@ func lint(vm *jsonnet.VM, node nodeWithLocation, errWriter *ErrorWriter) {
|
|||||||
for _, err := range ec.Errs {
|
for _, err := range ec.Errs {
|
||||||
errWriter.writeError(vm, err)
|
errWriter.writeError(vm, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImports(vm *jsonnet.VM, node nodeWithLocation, roots map[string]ast.Node, errWriter *ErrorWriter) {
|
func getImports(vm *jsonnet.VM, node nodeWithLocation, roots map[string]ast.Node, errWriter *ErrorWriter) {
|
||||||
@ -118,18 +131,24 @@ func getImports(vm *jsonnet.VM, node nodeWithLocation, roots map[string]ast.Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LintSnippet checks for problems in a single code snippet.
|
// LintSnippet checks for problems in code snippet(s).
|
||||||
func LintSnippet(vm *jsonnet.VM, output io.Writer, filename, code string) bool {
|
func LintSnippet(vm *jsonnet.VM, output io.Writer, snippets []Snippet) bool {
|
||||||
errWriter := ErrorWriter{
|
errWriter := ErrorWriter{
|
||||||
Writer: output,
|
Writer: output,
|
||||||
ErrorsFound: false,
|
ErrorsFound: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := jsonnet.SnippetToAST(filename, code)
|
var nodes []nodeWithLocation
|
||||||
|
for _, snippet := range snippets {
|
||||||
|
node, err := jsonnet.SnippetToAST(snippet.FileName, snippet.Code)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errWriter.writeError(vm, err.(errors.StaticError)) // ugly but true
|
errWriter.writeError(vm, err.(errors.StaticError)) // ugly but true
|
||||||
return true
|
} else {
|
||||||
|
nodes = append(nodes, nodeWithLocation{node, snippet.FileName})
|
||||||
}
|
}
|
||||||
lint(vm, nodeWithLocation{node, filename}, &errWriter)
|
}
|
||||||
|
|
||||||
|
lint(vm, nodes, &errWriter)
|
||||||
return errWriter.ErrorsFound
|
return errWriter.ErrorsFound
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func runTest(t *testing.T, test *linterTest, changedGoldensList *ChangedGoldensL
|
|||||||
|
|
||||||
var outBuilder strings.Builder
|
var outBuilder strings.Builder
|
||||||
|
|
||||||
errorsFound := LintSnippet(vm, &outBuilder, test.name, string(input))
|
errorsFound := LintSnippet(vm, &outBuilder, []Snippet{Snippet{FileName: test.name, Code: string(input)}})
|
||||||
|
|
||||||
outData := outBuilder.String()
|
outData := outBuilder.String()
|
||||||
|
|
||||||
@ -72,6 +72,40 @@ func runTest(t *testing.T, test *linterTest, changedGoldensList *ChangedGoldensL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runTests(t *testing.T, tests []*linterTest) {
|
||||||
|
read := func(file string) []byte {
|
||||||
|
bytz, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("reading file: %s: %v", file, err)
|
||||||
|
}
|
||||||
|
return bytz
|
||||||
|
}
|
||||||
|
|
||||||
|
vm := jsonnet.MakeVM()
|
||||||
|
|
||||||
|
var snippets []Snippet
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
input := read(test.input)
|
||||||
|
|
||||||
|
snippets = append(snippets, Snippet{FileName: test.name, Code: string(input)})
|
||||||
|
}
|
||||||
|
|
||||||
|
var outBuilder strings.Builder
|
||||||
|
|
||||||
|
errorsFound := LintSnippet(vm, &outBuilder, snippets)
|
||||||
|
|
||||||
|
outData := outBuilder.String()
|
||||||
|
|
||||||
|
if outData == "" && errorsFound {
|
||||||
|
t.Error(fmt.Errorf("return value indicates problems present, but no output was produced"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if outData != "" && !errorsFound {
|
||||||
|
t.Error(fmt.Errorf("return value indicates no problems, but output is not empty:\n%v", outData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLinter(t *testing.T) {
|
func TestLinter(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -122,4 +156,8 @@ func TestLinter(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Run("passing multiple input files", func(t *testing.T) {
|
||||||
|
runTests(t, tests)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user