-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
cmd_validate_json.go
173 lines (138 loc) · 4.03 KB
/
cmd_validate_json.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"strings"
)
// Structure for our options and state.
type validateJSONCommand struct {
// comma-separated list of files to exclude, as set by the
// command-line flag.
exclude string
// an array of patterns to exclude, calculated from the
// exclude setting above.
excluded []string
// Should we report on what we're testing.
verbose bool
}
// Arguments adds per-command args to the object.
func (vj *validateJSONCommand) Arguments(f *flag.FlagSet) {
f.BoolVar(&vj.verbose, "verbose", false, "Should we be verbose")
f.StringVar(&vj.exclude, "exclude", "", "Comma-separated list of patterns to exclude files from the check")
}
// Info returns the name of this subcommand.
func (vj *validateJSONCommand) Info() (string, string) {
return "validate-json", `Validate all JSON files for syntax.
Details:
This command allows you to validate JSON files, by default searching
recursively beneath the current directory for all files which match
the pattern '*.json'.
If you prefer you may specify a number of directories or files:
- Any file specified will be checked.
- Any directory specified will be recursively scanned for matching files.
- Files that do not have a '.json' suffix will be ignored.
Example:
$ sysbox validate-json -verbose file1.json file2.json ..
$ sysbox validate-json -exclude=foo /dir/1/path /file/1/path ..
`
}
// Validate a single file
func (vj *validateJSONCommand) validateFile(path string) error {
// Exclude this file? Based on the supplied list though?
for _, ex := range vj.excluded {
if strings.Contains(path, ex) {
if vj.verbose {
fmt.Printf("SKIPPED\t%s - matched '%s'\n", path, ex)
}
return nil
}
}
// Read the file-contents
data, err := os.ReadFile(path)
if err != nil {
return err
}
// Deserialize - receiving an error if that failed.
var result interface{}
err = json.Unmarshal(data, &result)
// Show the error if there was one, but otherwise only show
// the success if running verbosely.
if err != nil {
fmt.Printf("ERROR\t%s - %s\n", path, err.Error())
} else {
if vj.verbose {
fmt.Printf("OK\t%s\n", path)
}
}
return err
}
// Execute is invoked if the user specifies `validate-json` as the subcommand.
func (vj *validateJSONCommand) Execute(args []string) int {
// Did we find at least one file with an error?
failed := false
// Create our array of excluded patterns if something
// should be excluded.
if vj.exclude != "" {
vj.excluded = strings.Split(vj.exclude, ",")
}
// Add a fake argument if nothing is present, because we
// want to process the current directory (recursively) by default.
if len(args) < 1 {
args = append(args, ".")
}
// We can handle file/directory names as arguments. If a
// directory is specified then we process it recursively.
//
// We'll start by building up a list of all the files to test,
// before we begin the process of testing. We'll make sure
// our list is unique to cut down on any unnecessary I/O.
todo := make(map[string]bool)
// For each argument ..
for _, arg := range args {
// Check that it actually exists.
info, err := os.Stat(arg)
if os.IsNotExist(err) {
fmt.Printf("The path does not exist: %s\n", arg)
continue
}
// Error?
if err != nil {
fmt.Printf("Failed to stat(%s): %s\n", arg, err.Error())
continue
}
// A directory?
if info.Mode().IsDir() {
// Find suitable entries in the directory
files, err := FindFiles(arg, []string{".json"})
if err != nil {
fmt.Printf("Error finding files in %s: %s\n", arg, err.Error())
continue
}
// Then record each one.
for _, ent := range files {
todo[ent] = true
}
} else {
// OK the entry we were given is just a file,
// so we'll save the path away.
todo[arg] = true
}
}
//
// Now we have a list of files to process.
//
for file := range todo {
// Run the validation, and note the result
err := vj.validateFile(file)
if err != nil {
failed = true
}
}
// Setup a suitable exit-code
if failed {
return 1
}
return 0
}