Skip to content

Commit 0c2c8f0

Browse files
authored
Implement --only for lint to opt-in to specific rules (#436)
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent 77b0676 commit 0c2c8f0

11 files changed

+208
-4
lines changed

docs/lint.markdown

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ Linting
55
jsonschema lint [schemas-or-directories...] [--http/-h] [--fix/-f]
66
[--json/-j] [--verbose/-v] [--resolve/-r <schemas-or-directories> ...]
77
[--extension/-e <extension>] [--ignore/-i <schemas-or-directories>]
8-
[--exclude/-x <rule-name>] [--list/-l] [--default-dialect/-d <uri>]
8+
[--exclude/-x <rule-name>] [--only/-o <rule-name>] [--list/-l]
9+
[--default-dialect/-d <uri>]
910
```
1011

1112
JSON Schema is a surprisingly expressive schema language. Like with traditional
@@ -81,6 +82,12 @@ jsonschema lint path/to/my/schema_1.json path/to/my/schema_2.json
8182
jsonschema lint path/to/my/schema.json --exclude enum_with_type --exclude const_with_type
8283
```
8384

85+
### Lint with only a set of preselected rules
86+
87+
```sh
88+
jsonschema lint path/to/my/schema.json --only enum_with_type --only const_with_type
89+
```
90+
8491
### Lint with JSON output
8592

8693
```sh

src/command_lint.cc

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,30 @@ auto sourcemeta::jsonschema::cli::lint(const sourcemeta::core::Options &options)
9898
bundle.add<sourcemeta::blaze::ValidDefault>(
9999
sourcemeta::blaze::default_schema_compiler);
100100

101-
if (options.contains("exclude")) {
101+
if (options.contains("only")) {
102+
if (options.contains("exclude")) {
103+
std::cerr << "error: Cannot use --only and --exclude at the same time\n";
104+
return EXIT_FAILURE;
105+
}
106+
107+
std::unordered_set<std::string_view> blacklist;
108+
for (const auto &entry : bundle) {
109+
blacklist.emplace(entry.first);
110+
}
111+
112+
for (const auto &only : options.at("only")) {
113+
log_verbose(options) << "Only enabling rule: " << only << "\n";
114+
if (blacklist.erase(only) == 0) {
115+
std::cerr << "error: The following linting rule does not exist\n";
116+
std::cerr << " " << only << "\n";
117+
return EXIT_FAILURE;
118+
}
119+
}
120+
121+
for (const auto &name : blacklist) {
122+
bundle.remove(std::string{name});
123+
}
124+
} else if (options.contains("exclude")) {
102125
disable_lint_rules(bundle, options, options.at("exclude").cbegin(),
103126
options.at("exclude").cend());
104127
}

src/main.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Global Options:
7373
7474
lint [schemas-or-directories...] [--fix/-f] [--json/-j]
7575
[--extension/-e <extension>] [--ignore/-i <schemas-or-directories>]
76-
[--exclude/-x <rule-name>] [--list/-l]
76+
[--exclude/-x <rule-name>] [--only/-o <rule-name>] [--list/-l]
7777
7878
Lint the input schemas and potentially fix the reported issues.
7979
The --fix/-f option is not supported when passing YAML schemas.
@@ -134,6 +134,7 @@ auto jsonschema_main(const std::string &program, const std::string &command,
134134
app.flag("list", {"l"});
135135
app.option("extension", {"e"});
136136
app.option("exclude", {"x"});
137+
app.option("only", {"o"});
137138
app.option("ignore", {"i"});
138139
app.parse(argc, argv, {.skip = 1});
139140
return sourcemeta::jsonschema::cli::lint(app);

test/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,15 @@ add_jsonschema_test_unix(lint/pass_lint_examples_nested_with_id)
256256
add_jsonschema_test_unix(lint/pass_lint_list_exclude)
257257
add_jsonschema_test_unix(lint/pass_lint_list_long)
258258
add_jsonschema_test_unix(lint/pass_lint_list_short)
259+
add_jsonschema_test_unix(lint/pass_lint_list_only_one)
260+
add_jsonschema_test_unix(lint/pass_lint_list_only_many)
259261
add_jsonschema_test_unix(lint/pass_lint_fix_no_reformat)
260262
add_jsonschema_test_unix(lint/pass_lint_directory_resolve_many)
261263
add_jsonschema_test_unix(lint/pass_lint_direct_custom_extension)
264+
add_jsonschema_test_unix(lint/fail_lint_only)
265+
add_jsonschema_test_unix(lint/fail_lint_only_with_exclude)
266+
add_jsonschema_test_unix(lint/fail_lint_only_unknown)
267+
add_jsonschema_test_unix(lint/fail_lint_only_unknown_verbose)
262268

263269
# Encode
264270
add_jsonschema_test_unix(encode/pass_schema_less)

test/lint/fail_lint_only.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
TMP="$(mktemp -d)"
7+
clean() { rm -rf "$TMP"; }
8+
trap clean EXIT
9+
10+
cat << 'EOF' > "$TMP/schema.json"
11+
{
12+
"$schema": "https://json-schema.org/draft/2020-12/schema",
13+
"type": "string",
14+
"contentMediaType": "application/json",
15+
"enum": [ "foo", "bar" ]
16+
}
17+
EOF
18+
19+
"$1" lint "$TMP/schema.json" >"$TMP/stderr.txt" 2>&1 && CODE="$?" || CODE="$?"
20+
test "$CODE" = "1" || exit 1
21+
22+
cat << EOF > "$TMP/expected.txt"
23+
$(realpath "$TMP")/schema.json:
24+
The \`contentMediaType\` keyword is meaningless without the presence of the \`contentEncoding\` keyword (content_media_type_without_encoding)
25+
at schema location ""
26+
$(realpath "$TMP")/schema.json:
27+
Setting \`type\` alongside \`enum\` is considered an anti-pattern, as the enumeration choices already imply their respective types (enum_with_type)
28+
at schema location ""
29+
EOF
30+
31+
diff "$TMP/stderr.txt" "$TMP/expected.txt"
32+
33+
"$1" lint "$TMP/schema.json" --only content_media_type_without_encoding >"$TMP/stderr.txt" 2>&1 && CODE="$?" || CODE="$?"
34+
35+
cat << EOF > "$TMP/expected.txt"
36+
$(realpath "$TMP")/schema.json:
37+
The \`contentMediaType\` keyword is meaningless without the presence of the \`contentEncoding\` keyword (content_media_type_without_encoding)
38+
at schema location ""
39+
EOF
40+
41+
diff "$TMP/stderr.txt" "$TMP/expected.txt"

test/lint/fail_lint_only_unknown.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
TMP="$(mktemp -d)"
7+
clean() { rm -rf "$TMP"; }
8+
trap clean EXIT
9+
10+
cat << 'EOF' > "$TMP/schema.json"
11+
{
12+
"$schema": "http://json-schema.org/draft-04/schema#",
13+
"type": "string",
14+
"enum": [ "foo" ]
15+
}
16+
EOF
17+
18+
"$1" lint "$TMP/schema.json" --only foobarbaz \
19+
>"$TMP/stderr.txt" 2>&1 && CODE="$?" || CODE="$?"
20+
test "$CODE" = "1" || exit 1
21+
22+
cat << EOF > "$TMP/expected.txt"
23+
error: The following linting rule does not exist
24+
foobarbaz
25+
EOF
26+
27+
diff "$TMP/stderr.txt" "$TMP/expected.txt"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
TMP="$(mktemp -d)"
7+
clean() { rm -rf "$TMP"; }
8+
trap clean EXIT
9+
10+
cat << 'EOF' > "$TMP/schema.json"
11+
{
12+
"$schema": "http://json-schema.org/draft-04/schema#",
13+
"type": "string",
14+
"enum": [ "foo" ]
15+
}
16+
EOF
17+
18+
"$1" lint "$TMP/schema.json" --only foobarbaz --verbose \
19+
>"$TMP/stderr.txt" 2>&1 && CODE="$?" || CODE="$?"
20+
test "$CODE" = "1" || exit 1
21+
22+
cat << EOF > "$TMP/expected.txt"
23+
Only enabling rule: foobarbaz
24+
error: The following linting rule does not exist
25+
foobarbaz
26+
EOF
27+
28+
diff "$TMP/stderr.txt" "$TMP/expected.txt"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
TMP="$(mktemp -d)"
7+
clean() { rm -rf "$TMP"; }
8+
trap clean EXIT
9+
10+
cat << 'EOF' > "$TMP/schema.json"
11+
{
12+
"$schema": "http://json-schema.org/draft-04/schema#",
13+
"type": "string",
14+
"enum": [ "foo" ]
15+
}
16+
EOF
17+
18+
"$1" lint "$TMP/schema.json" \
19+
--only enum_with_type --exclude const_with_type --verbose \
20+
>"$TMP/stderr.txt" 2>&1 && CODE="$?" || CODE="$?"
21+
test "$CODE" = "1" || exit 1
22+
23+
cat << EOF > "$TMP/expected.txt"
24+
error: Cannot use --only and --exclude at the same time
25+
EOF
26+
27+
diff "$TMP/stderr.txt" "$TMP/expected.txt"

test/lint/pass_lint_list_only_many.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
TMP="$(mktemp -d)"
7+
clean() { rm -rf "$TMP"; }
8+
trap clean EXIT
9+
10+
"$1" lint --list --only then_empty --only then_without_if > "$TMP/output.txt"
11+
12+
cat << 'EOF' > "$TMP/expected.txt"
13+
then_empty
14+
Setting the `then` keyword to the empty schema does not add any further constraint
15+
16+
then_without_if
17+
The `then` keyword is meaningless without the presence of the `if` keyword
18+
19+
Number of rules: 2
20+
EOF
21+
22+
diff "$TMP/output.txt" "$TMP/expected.txt"

test/lint/pass_lint_list_only_one.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
TMP="$(mktemp -d)"
7+
clean() { rm -rf "$TMP"; }
8+
trap clean EXIT
9+
10+
"$1" lint --list --only definitions_to_defs > "$TMP/output.txt"
11+
12+
cat << 'EOF' > "$TMP/expected.txt"
13+
definitions_to_defs
14+
`definitions` was superseded by `$defs` in 2019-09 and later versions
15+
16+
Number of rules: 1
17+
EOF
18+
19+
diff "$TMP/output.txt" "$TMP/expected.txt"

0 commit comments

Comments
 (0)