From 25ae5de5a2c87e509afa13032e487b83bd822b9a Mon Sep 17 00:00:00 2001 From: eunwoo song Date: Sun, 6 Sep 2026 21:24:17 +0900 Subject: [PATCH] Add multitool schema validation --- .pre-commit-hooks.yaml | 11 + CHANGELOG.rst | 2 +- docs/precommit_usage.rst | 14 ++ docs/usage.rst | 1 + .../builtin_schemas/vendor/README.md | 5 + .../vendor/licenses/LICENSE.multitool | 201 ++++++++++++++++++ .../builtin_schemas/vendor/multitool.json | 133 ++++++++++++ .../vendor/sha256/multitool.sha256 | 1 + src/check_jsonschema/catalog.py | 14 ++ tests/acceptance/test_hook_file_matches.py | 11 + .../negative/multitool/multitool.lock.json | 11 + .../positive/multitool/multitool.lock.json | 14 ++ 12 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 src/check_jsonschema/builtin_schemas/vendor/licenses/LICENSE.multitool create mode 100644 src/check_jsonschema/builtin_schemas/vendor/multitool.json create mode 100644 src/check_jsonschema/builtin_schemas/vendor/sha256/multitool.sha256 create mode 100644 tests/example-files/hooks/negative/multitool/multitool.lock.json create mode 100644 tests/example-files/hooks/positive/multitool/multitool.lock.json diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 28a2597d5..ce7061983 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -272,6 +272,17 @@ )$ types: [yaml] +# this hook is autogenerated from a script +# to modify this hook, update `src/check_jsonschema/catalog.py` +# and run `just generate-hooks` or `tox run -e generate-hooks-config` +- id: check-multitool + name: Validate multitool lockfiles + description: 'Validate multitool lockfiles against the schema provided by rules_multitool' + entry: check-jsonschema --builtin-schema vendor.multitool + language: python + files: (^|.*/)multitool\.lock\.json$ + types: [json] + # this hook is autogenerated from a script # to modify this hook, update `src/check_jsonschema/catalog.py` # and run `just generate-hooks` or `tox run -e generate-hooks-config` diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9d030bcd9..ae5ff0ec6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,7 +10,7 @@ Unreleased .. vendor-insert-here -- Update vendored schemas: bitbucket-pipelines, mergify, renovate (2026-08-16) +- Update vendored schemas: bitbucket-pipelines, mergify, multitool, renovate (2026-09-06) 0.38.0 ------ diff --git a/docs/precommit_usage.rst b/docs/precommit_usage.rst index 02e1ae6a2..fe6d5ae32 100644 --- a/docs/precommit_usage.rst +++ b/docs/precommit_usage.rst @@ -323,6 +323,20 @@ Validate Mergify config against the schema provided by SchemaStore - id: check-mergify +``check-multitool`` +~~~~~~~~~~~~~~~~~~~ + +Validate multitool lockfiles against the schema provided by rules_multitool + +.. code-block:: yaml + :caption: example config + + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.38.0 + hooks: + - id: check-multitool + + ``check-readthedocs`` ~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/usage.rst b/docs/usage.rst index 3031c9921..98d3ed28d 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -106,6 +106,7 @@ SchemaStore and other sources: - ``vendor.gitlab-ci`` - ``vendor.meltano`` - ``vendor.mergify`` +- ``vendor.multitool`` - ``vendor.readthedocs`` - ``vendor.renovate`` - ``vendor.snapcraft`` diff --git a/src/check_jsonschema/builtin_schemas/vendor/README.md b/src/check_jsonschema/builtin_schemas/vendor/README.md index c47e81d67..ba22cd7ea 100644 --- a/src/check_jsonschema/builtin_schemas/vendor/README.md +++ b/src/check_jsonschema/builtin_schemas/vendor/README.md @@ -39,3 +39,8 @@ for their 'pipeline-schema' repo. The Taskfile schema is provided by Task and licensed under the license for their 'task' repo. + +### multitool + +The multitool lockfile schema is provided by bazel-contrib/rules_multitool and +licensed under the Apache License 2.0. diff --git a/src/check_jsonschema/builtin_schemas/vendor/licenses/LICENSE.multitool b/src/check_jsonschema/builtin_schemas/vendor/licenses/LICENSE.multitool new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/check_jsonschema/builtin_schemas/vendor/licenses/LICENSE.multitool @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/check_jsonschema/builtin_schemas/vendor/multitool.json b/src/check_jsonschema/builtin_schemas/vendor/multitool.json new file mode 100644 index 000000000..ab3278920 --- /dev/null +++ b/src/check_jsonschema/builtin_schemas/vendor/multitool.json @@ -0,0 +1,133 @@ +{ + "$id": "https://github.com/theoremlp/rules_multitool/lockfile.schema.json", + "title": "Describes binaries and instructions for fetching them under Bazel", + "type": "object", + "definitions": { + "supported_os": { + "enum": ["macos", "linux", "windows"] + }, + "supported_cpu": { + "enum": ["x86_64", "arm64"] + }, + "file_type": { + "type": "object", + "properties": { + "kind": { "enum": ["file"] }, + "url": { "type": "string" }, + "sha256": { "type": "string" }, + "os": { "$ref": "#/definitions/supported_os" }, + "cpu": { "$ref": "#/definitions/supported_cpu" }, + "headers": { + "type": "object", + "docs": "headers to pass to the downloader (supported on Bazel >= 7.1.0)", + "additionalProperties": { + "type": "string" + } + }, + "auth_patterns": { + "type": "object", + "docs": "See https://bazel.build/rules/lib/repo/http#http_archive-auth_patterns", + "additionalProperties": { + "type": "string" + } + } + }, + "required": ["kind", "url", "os", "cpu"] + }, + "archive_type": { + "type": "object", + "properties": { + "kind": { "enum": ["archive"] }, + "url": { "type": "string" }, + "sha256": { "type": "string" }, + "file": { + "type": "string", + "docs": "archive root relative path to binary" + }, + "os": { "$ref": "#/definitions/supported_os" }, + "cpu": { "$ref": "#/definitions/supported_cpu" }, + "headers": { + "type": "object", + "docs": "headers to pass to the downloader (supported on Bazel >= 7.1.0)", + "additionalProperties": { + "type": "string" + } + }, + "type": { + "enum": [ + "zip", + "jar", + "war", + "aar", + "tar", + "tar.gz", + "tgz", + "tar.xz", + "txz", + ".tar.zst", + ".tzst", + "tar.bz2", + ".tbz", + ".ar", + ".deb" + ] + }, + "auth_patterns": { + "type": "object", + "docs": "See https://bazel.build/rules/lib/repo/http#http_archive-auth_patterns", + "additionalProperties": { + "type": "string" + } + } + }, + "required": ["kind", "url", "os", "cpu", "file"] + }, + "pkg_type": { + "type": "object", + "properties": { + "kind": { "enum": ["pkg"] }, + "url": { "type": "string" }, + "sha256": { "type": "string" }, + "file": { + "type": "string", + "docs": "pkg archive root relative path to binary" + }, + "os": { "$ref": "#/definitions/supported_os" }, + "cpu": { "$ref": "#/definitions/supported_cpu" }, + "headers": { + "type": "object", + "docs": "headers to pass to the downloader (supported on Bazel >= 7.1.0)", + "additionalProperties": { + "type": "string" + } + }, + "auth_patterns": { + "type": "object", + "docs": "See https://bazel.build/rules/lib/repo/http#http_archive-auth_patterns", + "additionalProperties": { + "type": "string" + } + } + }, + "required": ["kind", "url", "os", "cpu", "file"] + } + }, + "properties": { + "$schema": { "type": "string" } + }, + "additionalProperties": { + "type": "object", + "properties": { + "binaries": { + "type": "array", + "items": { + "anyOf": [ + { "$ref": "#/definitions/file_type" }, + { "$ref": "#/definitions/archive_type" }, + { "$ref": "#/definitions/pkg_type" } + ] + } + } + } + } +} diff --git a/src/check_jsonschema/builtin_schemas/vendor/sha256/multitool.sha256 b/src/check_jsonschema/builtin_schemas/vendor/sha256/multitool.sha256 new file mode 100644 index 000000000..fd85d6439 --- /dev/null +++ b/src/check_jsonschema/builtin_schemas/vendor/sha256/multitool.sha256 @@ -0,0 +1 @@ +49bc6cedd6dfe9a0f58c1ece0a17b962ff0b1c4fa39f0c3727cb2b0d76de4b7d \ No newline at end of file diff --git a/src/check_jsonschema/catalog.py b/src/check_jsonschema/catalog.py index 80cbd7e9d..76a8b79b4 100644 --- a/src/check_jsonschema/catalog.py +++ b/src/check_jsonschema/catalog.py @@ -282,6 +282,20 @@ def _gitlab_raw_url(repo: str, file_path: str, ref: str) -> str: "types": "yaml", }, }, + "multitool": { + "url": _githubusercontent_url( + "bazel-contrib", "rules_multitool", "main", "lockfile.schema.json" + ), + "hook_config": { + "name": "Validate multitool lockfiles", + "description": ( + "Validate multitool lockfiles against the schema provided by " + "rules_multitool" + ), + "files": r"(^|.*/)multitool\.lock\.json$", + "types": "json", + }, + }, "readthedocs": { "url": _githubusercontent_url( "readthedocs", diff --git a/tests/acceptance/test_hook_file_matches.py b/tests/acceptance/test_hook_file_matches.py index 7c007a707..a9f4b460b 100644 --- a/tests/acceptance/test_hook_file_matches.py +++ b/tests/acceptance/test_hook_file_matches.py @@ -141,6 +141,17 @@ def get_hook_config(hookid): "meltano-manifest.yml", ), }, + "check-multitool": { + "good": ( + "multitool.lock.json", + "config/multitool.lock.json", + ), + "bad": ( + "multitool.json", + "multitool.lock.yaml", + "config/multitool.lock.json.example", + ), + }, "check-dependabot": { "good": (".github/dependabot.yml", ".github/dependabot.yaml"), "bad": (".dependabot.yaml", ".dependabot.yml"), diff --git a/tests/example-files/hooks/negative/multitool/multitool.lock.json b/tests/example-files/hooks/negative/multitool/multitool.lock.json new file mode 100644 index 000000000..8fe792423 --- /dev/null +++ b/tests/example-files/hooks/negative/multitool/multitool.lock.json @@ -0,0 +1,11 @@ +{ + "ruff": { + "binaries": [ + { + "kind": "file", + "url": "https://example.com/ruff", + "cpu": "x86_64" + } + ] + } +} diff --git a/tests/example-files/hooks/positive/multitool/multitool.lock.json b/tests/example-files/hooks/positive/multitool/multitool.lock.json new file mode 100644 index 000000000..254020225 --- /dev/null +++ b/tests/example-files/hooks/positive/multitool/multitool.lock.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://github.com/theoremlp/rules_multitool/lockfile.schema.json", + "ruff": { + "binaries": [ + { + "kind": "file", + "url": "https://example.com/ruff", + "sha256": "0123456789abcdef", + "os": "linux", + "cpu": "x86_64" + } + ] + } +}