From e072815ab35faa8badfbaefc22c45262214b6165 Mon Sep 17 00:00:00 2001 From: vivodi <103735539+vivodi@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:27:42 +0800 Subject: [PATCH] feat(pre-commit): add Codecov config schema and pre-commit hook --- .pre-commit-hooks.yaml | 14 + CHANGELOG.rst | 1 + docs/precommit_usage.rst | 14 + docs/usage.rst | 1 + .../builtin_schemas/vendor/codecov.json | 620 ++++++++++++++++++ .../vendor/sha256/codecov.sha256 | 1 + src/check_jsonschema/catalog.py | 10 + tests/acceptance/test_hook_file_matches.py | 17 + .../hooks/positive/codecov/.codecov.yml | 40 ++ .../hooks/positive/codecov/codecov.yml | 13 + 10 files changed, 731 insertions(+) create mode 100644 src/check_jsonschema/builtin_schemas/vendor/codecov.json create mode 100644 src/check_jsonschema/builtin_schemas/vendor/sha256/codecov.sha256 create mode 100644 tests/example-files/hooks/positive/codecov/.codecov.yml create mode 100644 tests/example-files/hooks/positive/codecov/codecov.yml diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 9b5ad6f98..4e14e1063 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -89,6 +89,20 @@ files: ^cloudbuild\.(yml|yaml|json)$ types_or: [json,yaml] +# this hook is autogenerated from a script +# to modify this hook, update `src/check_jsonschema/catalog.py` +# and run `make generate-hooks` or `tox run -e generate-hooks-config` +- id: check-codecov + name: Validate Codecov config + description: 'Validate Codecov config against the schema provided by SchemaStore' + entry: check-jsonschema --builtin-schema vendor.codecov + language: python + files: > + (?x)^( + ^((\.github|dev)/)?\.?codecov\.ya?ml$ + )$ + types: [yaml] + # this hook is autogenerated from a script # to modify this hook, update `src/check_jsonschema/catalog.py` # and run `make generate-hooks` or `tox run -e generate-hooks-config` diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8c9b797d9..d2962bd1a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,7 @@ Unreleased .. vendor-insert-here - Add GitHub issue form schema and pre-commit hook (:issue:`588`). +- Add Codecov config schema and pre-commit hook. 0.33.3 ------ diff --git a/docs/precommit_usage.rst b/docs/precommit_usage.rst index a96ef3676..1acd8c7e6 100644 --- a/docs/precommit_usage.rst +++ b/docs/precommit_usage.rst @@ -127,6 +127,20 @@ Validate Google Cloud Build config against the schema provided by SchemaStore - id: check-cloudbuild +``check-codecov`` +~~~~~~~~~~~~~~~~~ + +Validate Codecov config against the schema provided by SchemaStore + +.. code-block:: yaml + :caption: example config + + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.33.3 + hooks: + - id: check-codecov + + ``check-compose-spec`` ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/usage.rst b/docs/usage.rst index b6445c8e5..0655e05b0 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -92,6 +92,7 @@ SchemaStore and other sources: - ``vendor.buildkite`` - ``vendor.circle-ci`` - ``vendor.cloudbuild`` +- ``vendor.codecov`` - ``vendor.compose-spec`` - ``vendor.dependabot`` - ``vendor.drone-ci`` diff --git a/src/check_jsonschema/builtin_schemas/vendor/codecov.json b/src/check_jsonschema/builtin_schemas/vendor/codecov.json new file mode 100644 index 000000000..98decea44 --- /dev/null +++ b/src/check_jsonschema/builtin_schemas/vendor/codecov.json @@ -0,0 +1,620 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/codecov", + "definitions": { + "default": { + "$comment": "See https://docs.codecov.com/docs/commit-status#basic-configuration", + "properties": { + "target": { + "type": ["string", "number"], + "pattern": "^(([0-9]+\\.?[0-9]*|\\.[0-9]+)%?|auto)$", + "default": "auto" + }, + "threshold": { + "type": "string", + "default": "0%", + "pattern": "^([0-9]+\\.?[0-9]*|\\.[0-9]+)%?$" + }, + "base": { + "type": "string", + "default": "auto", + "deprecated": true + }, + "flags": { + "type": "array", + "default": [] + }, + "paths": { + "type": ["array", "string"], + "default": [] + }, + "branches": { + "type": "array", + "default": [] + }, + "if_not_found": { + "type": "string", + "enum": ["failure", "success"], + "default": "success" + }, + "informational": { + "type": "boolean", + "default": false + }, + "only_pulls": { + "type": "boolean", + "default": false + }, + "if_ci_failed": { + "type": "string", + "enum": ["error", "success"] + }, + "flag_coverage_not_uploaded_behavior": { + "type": "string", + "enum": ["include", "exclude", "pass"] + } + } + }, + "flag": { + "type": "object", + "properties": { + "joined": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "ignore": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "assume": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + } + } + }, + "layout": { + "anyOf": [ + {}, + { + "enum": [ + "header", + "footer", + "diff", + "file", + "files", + "flag", + "flags", + "reach", + "sunburst", + "uncovered" + ] + } + ] + }, + "notification": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "description": "Schema for codecov.yml files.", + "properties": { + "codecov": { + "description": "See https://docs.codecov.io/docs/codecov-yaml for details", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "bot": { + "description": "Team bot. See https://docs.codecov.io/docs/team-bot for details", + "type": "string" + }, + "branch": { + "type": "string" + }, + "ci": { + "description": "Detecting CI services. See https://docs.codecov.io/docs/detecting-ci-services for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "assume_all_flags": { + "type": "boolean" + }, + "strict_yaml_branch": { + "type": "string" + }, + "max_report_age": { + "type": ["string", "integer", "boolean"] + }, + "disable_default_path_fixes": { + "type": "boolean" + }, + "require_ci_to_pass": { + "type": "boolean" + }, + "allow_pseudo_compare": { + "type": "boolean" + }, + "archive": { + "type": "object", + "properties": { + "uploads": { + "type": "boolean" + } + } + }, + "notify": { + "type": "object", + "properties": { + "after_n_builds": { + "type": "integer" + }, + "countdown": { + "type": "integer" + }, + "delay": { + "type": "integer" + }, + "wait_for_ci": { + "type": "boolean" + } + } + }, + "ui": { + "type": "object", + "properties": { + "hide_density": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + }, + "hide_complexity": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + }, + "hide_contextual": { + "type": "boolean" + }, + "hide_sunburst": { + "type": "boolean" + }, + "hide_search": { + "type": "boolean" + } + } + } + } + }, + "coverage": { + "description": "Coverage configuration. See https://docs.codecov.io/docs/coverage-configuration for details.", + "type": "object", + "properties": { + "precision": { + "type": "integer", + "minimum": 0, + "maximum": 5 + }, + "round": { + "enum": ["down", "up", "nearest"] + }, + "range": { + "type": "string" + }, + "notify": { + "description": "Notifications. See https://docs.codecov.io/docs/notifications for details.", + "type": "object", + "properties": { + "irc": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "channel": { + "type": "string" + }, + "password": { + "type": "string" + }, + "nickserv_password": { + "type": "string" + }, + "notice": { + "type": "boolean" + } + } + }, + "slack": { + "description": "Slack. See https://docs.codecov.io/docs/notifications#section-slack for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "attachments": { + "$ref": "#/definitions/layout" + } + } + }, + "gitter": { + "description": "Gitter. See https://docs.codecov.io/docs/notifications#section-gitter for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "hipchat": { + "description": "Hipchat. See https://docs.codecov.io/docs/notifications#section-hipchat for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "card": { + "type": "boolean" + }, + "notify": { + "type": "boolean" + } + } + }, + "webhook": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "email": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "layout": { + "$ref": "#/definitions/layout" + }, + "+to": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "status": { + "description": "Commit status. See https://docs.codecov.io/docs/commit-status for details.", + "type": ["boolean", "object"], + "additionalProperties": false, + "properties": { + "default_rules": { + "type": "object" + }, + "project": { + "properties": { + "default": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + }, + "additionalProperties": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + }, + "patch": { + "anyOf": [ + { + "$ref": "#/definitions/default", + "type": "object" + }, + { + "type": "string", + "enum": ["off"] + }, + { + "type": "boolean" + } + ] + }, + "changes": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + } + } + } + }, + "ignore": { + "description": "Ignoring paths. see https://docs.codecov.io/docs/ignoring-paths for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "fixes": { + "description": "Fixing paths. See https://docs.codecov.io/docs/fixing-paths for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "flags": { + "description": "Flags. See https://docs.codecov.io/docs/flags for details.", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/flag" + } + }, + { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/flag" + } + } + ] + }, + "comment": { + "description": "Pull request comments. See https://docs.codecov.io/docs/pull-request-comments for details.", + "oneOf": [ + { + "type": "object", + "properties": { + "layout": { + "$ref": "#/definitions/layout" + }, + "require_changes": { + "type": "boolean" + }, + "require_base": { + "type": "boolean" + }, + "require_head": { + "type": "boolean" + }, + "branches": { + "type": "array", + "items": { + "type": "string" + } + }, + "behavior": { + "enum": ["default", "once", "new", "spammy"] + }, + "flags": { + "type": "array", + "items": { + "$ref": "#/definitions/flag" + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "const": false + } + ] + }, + "github_checks": { + "description": "GitHub Checks. See https://docs.codecov.com/docs/github-checks for details.", + "anyOf": [ + { + "type": "object", + "properties": { + "annotations": { + "type": "boolean" + } + } + }, + { "type": "boolean" }, + { "type": "string", "enum": ["off"] } + ] + } + }, + "title": "JSON schema for Codecov configuration files", + "type": "object" +} diff --git a/src/check_jsonschema/builtin_schemas/vendor/sha256/codecov.sha256 b/src/check_jsonschema/builtin_schemas/vendor/sha256/codecov.sha256 new file mode 100644 index 000000000..a412c5762 --- /dev/null +++ b/src/check_jsonschema/builtin_schemas/vendor/sha256/codecov.sha256 @@ -0,0 +1 @@ +5bd8ea99586b82003c5dbaadc8fad6added566466221c065584f6db57e04238e \ No newline at end of file diff --git a/src/check_jsonschema/catalog.py b/src/check_jsonschema/catalog.py index faedf25f7..cc2d63fba 100644 --- a/src/check_jsonschema/catalog.py +++ b/src/check_jsonschema/catalog.py @@ -98,6 +98,16 @@ def _githubusercontent_url(owner: str, repo: str, ref: str, path: str) -> str: "types_or": ["json", "yaml"], }, }, + "codecov": { + "url": "https://www.schemastore.org/codecov.json", + "hook_config": { + "name": "Validate Codecov config", + "files": [ + r"^((\.github|dev)/)?\.?codecov\.ya?ml$", + ], + "types": "yaml", + }, + }, "compose-spec": { "url": _githubusercontent_url( "compose-spec", diff --git a/tests/acceptance/test_hook_file_matches.py b/tests/acceptance/test_hook_file_matches.py index c96211933..ab137ce15 100644 --- a/tests/acceptance/test_hook_file_matches.py +++ b/tests/acceptance/test_hook_file_matches.py @@ -80,6 +80,23 @@ def get_hook_config(hookid): "bamboo-specs/README.md", ), }, + "check-codecov": { + "good": ( + "codecov.yml", + "codecov.yaml", + ".codecov.yml", + ".codecov.yaml", + ".github/codecov.yml", + ".github/codecov.yaml", + ".github/.codecov.yml", + ".github/.codecov.yaml", + "dev/codecov.yml", + "dev/codecov.yaml", + "dev/.codecov.yml", + "dev/.codecov.yaml", + ), + "bad": (".gitlab/codecov.yml",), + }, "check-compose-spec": { "good": ( "compose.yml", diff --git a/tests/example-files/hooks/positive/codecov/.codecov.yml b/tests/example-files/hooks/positive/codecov/.codecov.yml new file mode 100644 index 000000000..e21d45ac7 --- /dev/null +++ b/tests/example-files/hooks/positive/codecov/.codecov.yml @@ -0,0 +1,40 @@ +codecov: + branch: master + notify: + after_n_builds: 13 + +coverage: + range: "95..100" + + status: + project: no + +flags: + library: + paths: + - aiohttp/ + configs: + paths: + - requirements/ + - ".git*" + - "*.toml" + - "*.yml" + changelog: + paths: + - CHANGES/ + - CHANGES.rst + docs: + paths: + - docs/ + - "*.md" + - "*.rst" + - "*.txt" + tests: + paths: + - tests/ + tools: + paths: + - tools/ + third-party: + paths: + - vendor/ diff --git a/tests/example-files/hooks/positive/codecov/codecov.yml b/tests/example-files/hooks/positive/codecov/codecov.yml new file mode 100644 index 000000000..ddb8eb8ef --- /dev/null +++ b/tests/example-files/hooks/positive/codecov/codecov.yml @@ -0,0 +1,13 @@ +codecov: + notify: + after_n_builds: 13 + +coverage: + status: + patch: + default: + target: 100% + informational: true # Once the project achieves high code coverage, consider making it a mandatory check. + project: + default: + target: auto