From 9bb73b35387c076c463989177007160f9828b34d Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 7 Sep 2026 15:21:32 -0300 Subject: [PATCH 1/2] Support `--continue/-c` in the `metaschema` command Signed-off-by: Juan Cruz Viotti --- completion/jsonschema.bash | 2 +- completion/jsonschema.zsh | 1 + docs/metaschema.markdown | 2 +- src/command_metaschema.cc | 18 +++++++++- src/main.cc | 3 +- test/CMakeLists.txt | 2 ++ test/help_command.clitest | 2 +- test/help_option_long.clitest | 2 +- test/help_option_short.clitest | 2 +- .../fail_directory_continue.clitest | 35 +++++++++++++++++++ test/metaschema/fail_directory_stop.clitest | 34 ++++++++++++++++++ 11 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 test/metaschema/fail_directory_continue.clitest create mode 100644 test/metaschema/fail_directory_stop.clitest diff --git a/completion/jsonschema.bash b/completion/jsonschema.bash index e2b4b452..5b787433 100644 --- a/completion/jsonschema.bash +++ b/completion/jsonschema.bash @@ -112,7 +112,7 @@ _jsonschema() { fi ;; metaschema) - local options="--extension -e --ignore -i --trace -t" + local options="--extension -e --ignore -i --trace -t --continue -c" if [[ ${current} == -* ]] then COMPREPLY=( $(compgen -W "${options} ${global_options}" -- "${current}") ) diff --git a/completion/jsonschema.zsh b/completion/jsonschema.zsh index 10342e31..f6fc43dd 100644 --- a/completion/jsonschema.zsh +++ b/completion/jsonschema.zsh @@ -68,6 +68,7 @@ _jsonschema() { '(--extension -e)'{--extension,-e}'[Specify file extension]:extension:_jsonschema_extensions' \ '(--ignore -i)'{--ignore,-i}'[Ignore schemas or directories]:path:_files' \ '(--trace -t)'{--trace,-t}'[Enable trace output]' \ + '(--continue -c)'{--continue,-c}'[Report every failing schema]' \ '*:schema file:_files -g "*.json *.yaml *.yml"' ;; compile) diff --git a/docs/metaschema.markdown b/docs/metaschema.markdown index 93309fe1..a567dbe6 100644 --- a/docs/metaschema.markdown +++ b/docs/metaschema.markdown @@ -9,7 +9,7 @@ jsonschema metaschema [schemas-or-directories...] [--http/-h] [--verbose/-v] [--debug/-g] [--extension/-e ] [--header/-H ": "] [--resolve/-r ...] - [--ignore/-i ] [--trace/-t] + [--ignore/-i ] [--trace/-t] [--continue/-c] [--default-dialect/-d ] [--json/-j] [--format-assertion/-F] [--configuration/-C ] ``` diff --git a/src/command_metaschema.cc b/src/command_metaschema.cc index 547f7c83..289d5e55 100644 --- a/src/command_metaschema.cc +++ b/src/command_metaschema.cc @@ -10,6 +10,7 @@ #include // assert #include // std::cout, std::cerr +#include // std::next #include // std::map #include // std::ostringstream #include // std::string @@ -54,13 +55,18 @@ auto sourcemeta::jsonschema::metaschema( validate_http_headers(options); const auto trace{options.contains("trace")}; const auto json_output{options.contains("json")}; + const auto continue_on_error{options.contains("continue")}; ValidationSummary summary; sourcemeta::blaze::Evaluator evaluator; std::map cache; - for (const auto &entry : for_each_json(options, InputRequirement::NonEmpty)) { + const auto entries{for_each_json(options, InputRequirement::NonEmpty)}; + for (auto iterator{entries.cbegin()}; iterator != entries.cend(); + ++iterator) { + const auto &entry{*iterator}; + const auto failures_before{summary.failed}; summary.validated += 1; if (!entry.second.is_object() && !entry.second.is_boolean()) { throw NotSchemaError{entry.from_stdin ? stdin_path() @@ -199,12 +205,22 @@ auto sourcemeta::jsonschema::metaschema( sourcemeta::blaze::SchemaAnchorCollisionError>(entry.resolution_base, error); } + + if (summary.failed > failures_before && !continue_on_error) { + summary.stopped = std::next(iterator) != entries.cend(); + break; + } } if (!json_output && !trace) { print_summary(summary, options, std::cerr); } + if (summary.stopped) { + LOG_WARNING() + << "Stopped at first failure, pass --continue/-c to keep going\n"; + } + if (summary.failed > 0) { throw Fail{EXIT_EXPECTED_FAILURE}; } diff --git a/src/main.cc b/src/main.cc index c072a675..657d23f0 100644 --- a/src/main.cc +++ b/src/main.cc @@ -60,7 +60,7 @@ Global Options: metaschema [schemas-or-directories...] [--extension/-e ] [--ignore/-i ] [--trace/-t] - [--format-assertion/-F] + [--format-assertion/-F] [--continue/-c] Validate that a schema or a set of schemas are valid with respect to their metaschemas. @@ -227,6 +227,7 @@ auto jsonschema_main(const std::string &program, const std::string &command, if (command == "metaschema") { app.flag("trace", {"t"}); app.flag("format-assertion", {"F"}); + app.flag("continue", {"c"}); app.option("extension", {"e"}); app.option("ignore", {"i"}); app.parse(argc, argv, {.skip = 1}); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e93d87ef..8e8a6504 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -375,6 +375,8 @@ add_jsonschema_test(validate/pass_bundled_metaschema) add_jsonschema_test(metaschema/pass_trace) add_jsonschema_test(metaschema/fail_trace) add_jsonschema_test(metaschema/fail_directory) +add_jsonschema_test(metaschema/fail_directory_stop) +add_jsonschema_test(metaschema/fail_directory_continue) add_jsonschema_test(metaschema/fail_no_schemas_directory) add_jsonschema_test(metaschema/fail_no_schemas_directory_json) add_jsonschema_test(metaschema/fail_no_schemas_extension) diff --git a/test/help_command.clitest b/test/help_command.clitest index d13657c0..bfe2a24b 100644 --- a/test/help_command.clitest +++ b/test/help_command.clitest @@ -53,7 +53,7 @@ WRITE expected.txt UNTIL EOF 1> 1> metaschema [schemas-or-directories...] [--extension/-e ] 1> [--ignore/-i ] [--trace/-t] -1> [--format-assertion/-F] +1> [--format-assertion/-F] [--continue/-c] 1> 1> Validate that a schema or a set of schemas are valid with respect 1> to their metaschemas. diff --git a/test/help_option_long.clitest b/test/help_option_long.clitest index 27ed79e4..6ff66a95 100644 --- a/test/help_option_long.clitest +++ b/test/help_option_long.clitest @@ -53,7 +53,7 @@ WRITE expected.txt UNTIL EOF 1> 1> metaschema [schemas-or-directories...] [--extension/-e ] 1> [--ignore/-i ] [--trace/-t] -1> [--format-assertion/-F] +1> [--format-assertion/-F] [--continue/-c] 1> 1> Validate that a schema or a set of schemas are valid with respect 1> to their metaschemas. diff --git a/test/help_option_short.clitest b/test/help_option_short.clitest index 57e3dd0c..11992606 100644 --- a/test/help_option_short.clitest +++ b/test/help_option_short.clitest @@ -53,7 +53,7 @@ WRITE expected.txt UNTIL EOF 1> 1> metaschema [schemas-or-directories...] [--extension/-e ] 1> [--ignore/-i ] [--trace/-t] -1> [--format-assertion/-F] +1> [--format-assertion/-F] [--continue/-c] 1> 1> Validate that a schema or a set of schemas are valid with respect 1> to their metaschemas. diff --git a/test/metaschema/fail_directory_continue.clitest b/test/metaschema/fail_directory_continue.clitest new file mode 100644 index 00000000..1826f6e1 --- /dev/null +++ b/test/metaschema/fail_directory_continue.clitest @@ -0,0 +1,35 @@ +MAKE DIRECTORY schemas + +WRITE schemas/schema_1.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": "foo" +} +EOF + +WRITE schemas/schema_2.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": 1 +} +EOF + +// Validation failure +RUN metaschema schemas --continue --verbose STDIN /dev/null IN . INTO result_0.txt EXPECTING 2 + +WRITE expected_0.txt UNTIL EOF +2> fail: schemas/schema_1.json +2> error: Schema validation failure +2> The value was expected to be of type number but it was of type string +2> at instance location "/minimum" (line 3, column 3) +2> at evaluate path "/properties/minimum/type" +2> The object value was expected to validate against the 33 defined properties subschemas +2> at instance location "" (line 1, column 1) +2> at evaluate path "/properties" +2> ok: schemas/schema_2.json +2> matches http://json-schema.org/draft-04/schema# +2> +2> 2 validated, 1 passed, 1 failed +EOF + +COMPARE result_0.txt AGAINST expected_0.txt diff --git a/test/metaschema/fail_directory_stop.clitest b/test/metaschema/fail_directory_stop.clitest new file mode 100644 index 00000000..ef360ce1 --- /dev/null +++ b/test/metaschema/fail_directory_stop.clitest @@ -0,0 +1,34 @@ +MAKE DIRECTORY schemas + +WRITE schemas/schema_1.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": "foo" +} +EOF + +WRITE schemas/schema_2.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": 1 +} +EOF + +// Validation failure +RUN metaschema schemas --verbose STDIN /dev/null IN . INTO result_0.txt EXPECTING 2 + +WRITE expected_0.txt UNTIL EOF +2> fail: schemas/schema_1.json +2> error: Schema validation failure +2> The value was expected to be of type number but it was of type string +2> at instance location "/minimum" (line 3, column 3) +2> at evaluate path "/properties/minimum/type" +2> The object value was expected to validate against the 33 defined properties subschemas +2> at instance location "" (line 1, column 1) +2> at evaluate path "/properties" +2> +2> 1 validated, 0 passed, 1 failed +2> warning: Stopped at first failure, pass --continue/-c to keep going +EOF + +COMPARE result_0.txt AGAINST expected_0.txt From 8511e6b61067108c3001cb6e3ee88fc909045dd2 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 7 Sep 2026 15:39:14 -0300 Subject: [PATCH 2/2] Fix Signed-off-by: Juan Cruz Viotti --- docs/metaschema.markdown | 5 +- src/main.cc | 2 +- test/CMakeLists.txt | 2 + test/help_command.clitest | 2 +- test/help_option_long.clitest | 2 +- test/help_option_short.clitest | 2 +- .../fail_directory_continue_json.clitest | 47 +++++++++++++++++++ .../fail_directory_stop_json.clitest | 44 +++++++++++++++++ 8 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 test/metaschema/fail_directory_continue_json.clitest create mode 100644 test/metaschema/fail_directory_stop_json.clitest diff --git a/docs/metaschema.markdown b/docs/metaschema.markdown index a567dbe6..7e111736 100644 --- a/docs/metaschema.markdown +++ b/docs/metaschema.markdown @@ -9,8 +9,9 @@ jsonschema metaschema [schemas-or-directories...] [--http/-h] [--verbose/-v] [--debug/-g] [--extension/-e ] [--header/-H ": "] [--resolve/-r ...] - [--ignore/-i ] [--trace/-t] [--continue/-c] - [--default-dialect/-d ] [--json/-j] [--format-assertion/-F] + [--ignore/-i ] [--trace/-t] + [--default-dialect/-d ] [--json/-j] [--continue/-c] + [--format-assertion/-F] [--configuration/-C ] ``` diff --git a/src/main.cc b/src/main.cc index 657d23f0..0a2d2d96 100644 --- a/src/main.cc +++ b/src/main.cc @@ -60,7 +60,7 @@ Global Options: metaschema [schemas-or-directories...] [--extension/-e ] [--ignore/-i ] [--trace/-t] - [--format-assertion/-F] [--continue/-c] + [--continue/-c] [--format-assertion/-F] Validate that a schema or a set of schemas are valid with respect to their metaschemas. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8e8a6504..4dbda8b4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -376,7 +376,9 @@ add_jsonschema_test(metaschema/pass_trace) add_jsonschema_test(metaschema/fail_trace) add_jsonschema_test(metaschema/fail_directory) add_jsonschema_test(metaschema/fail_directory_stop) +add_jsonschema_test(metaschema/fail_directory_stop_json) add_jsonschema_test(metaschema/fail_directory_continue) +add_jsonschema_test(metaschema/fail_directory_continue_json) add_jsonschema_test(metaschema/fail_no_schemas_directory) add_jsonschema_test(metaschema/fail_no_schemas_directory_json) add_jsonschema_test(metaschema/fail_no_schemas_extension) diff --git a/test/help_command.clitest b/test/help_command.clitest index bfe2a24b..3a23db03 100644 --- a/test/help_command.clitest +++ b/test/help_command.clitest @@ -53,7 +53,7 @@ WRITE expected.txt UNTIL EOF 1> 1> metaschema [schemas-or-directories...] [--extension/-e ] 1> [--ignore/-i ] [--trace/-t] -1> [--format-assertion/-F] [--continue/-c] +1> [--continue/-c] [--format-assertion/-F] 1> 1> Validate that a schema or a set of schemas are valid with respect 1> to their metaschemas. diff --git a/test/help_option_long.clitest b/test/help_option_long.clitest index 6ff66a95..6fdb9476 100644 --- a/test/help_option_long.clitest +++ b/test/help_option_long.clitest @@ -53,7 +53,7 @@ WRITE expected.txt UNTIL EOF 1> 1> metaschema [schemas-or-directories...] [--extension/-e ] 1> [--ignore/-i ] [--trace/-t] -1> [--format-assertion/-F] [--continue/-c] +1> [--continue/-c] [--format-assertion/-F] 1> 1> Validate that a schema or a set of schemas are valid with respect 1> to their metaschemas. diff --git a/test/help_option_short.clitest b/test/help_option_short.clitest index 11992606..e336c2f6 100644 --- a/test/help_option_short.clitest +++ b/test/help_option_short.clitest @@ -53,7 +53,7 @@ WRITE expected.txt UNTIL EOF 1> 1> metaschema [schemas-or-directories...] [--extension/-e ] 1> [--ignore/-i ] [--trace/-t] -1> [--format-assertion/-F] [--continue/-c] +1> [--continue/-c] [--format-assertion/-F] 1> 1> Validate that a schema or a set of schemas are valid with respect 1> to their metaschemas. diff --git a/test/metaschema/fail_directory_continue_json.clitest b/test/metaschema/fail_directory_continue_json.clitest new file mode 100644 index 00000000..27b6254f --- /dev/null +++ b/test/metaschema/fail_directory_continue_json.clitest @@ -0,0 +1,47 @@ +MAKE DIRECTORY schemas + +WRITE schemas/schema_1.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": "foo" +} +EOF + +WRITE schemas/schema_2.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": 1 +} +EOF + +// Validation failure +RUN metaschema schemas --json --continue STDIN /dev/null IN . INTO result_0.txt EXPECTING 2 + +WRITE expected_0.txt UNTIL EOF +1> { +1> "valid": false, +1> "errors": [ +1> { +1> "keywordLocation": "/properties/minimum/type", +1> "absoluteKeywordLocation": "http://json-schema.org/draft-04/schema#/properties/minimum/type", +1> "instanceLocation": "/minimum", +1> "instancePosition": [ 3, 3, 3, 18 ], +1> "error": "The value was expected to be of type number but it was of type string" +1> }, +1> { +1> "keywordLocation": "/properties", +1> "absoluteKeywordLocation": "http://json-schema.org/draft-04/schema#/properties", +1> "instanceLocation": "", +1> "instancePosition": [ 1, 1, 4, 1 ], +1> "error": "The object value was expected to validate against the 33 defined properties subschemas" +1> } +1> ] +1> } +1> { +1> "valid": true +1> } +2> schemas/schema_1.json +2> schemas/schema_2.json +EOF + +COMPARE result_0.txt AGAINST expected_0.txt diff --git a/test/metaschema/fail_directory_stop_json.clitest b/test/metaschema/fail_directory_stop_json.clitest new file mode 100644 index 00000000..41bda2c5 --- /dev/null +++ b/test/metaschema/fail_directory_stop_json.clitest @@ -0,0 +1,44 @@ +MAKE DIRECTORY schemas + +WRITE schemas/schema_1.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": "foo" +} +EOF + +WRITE schemas/schema_2.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "minimum": 1 +} +EOF + +// Validation failure +RUN metaschema schemas --json STDIN /dev/null IN . INTO result_0.txt EXPECTING 2 + +WRITE expected_0.txt UNTIL EOF +1> { +1> "valid": false, +1> "errors": [ +1> { +1> "keywordLocation": "/properties/minimum/type", +1> "absoluteKeywordLocation": "http://json-schema.org/draft-04/schema#/properties/minimum/type", +1> "instanceLocation": "/minimum", +1> "instancePosition": [ 3, 3, 3, 18 ], +1> "error": "The value was expected to be of type number but it was of type string" +1> }, +1> { +1> "keywordLocation": "/properties", +1> "absoluteKeywordLocation": "http://json-schema.org/draft-04/schema#/properties", +1> "instanceLocation": "", +1> "instancePosition": [ 1, 1, 4, 1 ], +1> "error": "The object value was expected to validate against the 33 defined properties subschemas" +1> } +1> ] +1> } +2> schemas/schema_1.json +2> warning: Stopped at first failure, pass --continue/-c to keep going +EOF + +COMPARE result_0.txt AGAINST expected_0.txt