Skip to content

Commit

Permalink
Include links to documentation for schema validation errors
Browse files Browse the repository at this point in the history
Fixes: #3656
  • Loading branch information
ssbarnea committed Aug 24, 2023
1 parent c1b41f2 commit aa1efdb
Show file tree
Hide file tree
Showing 15 changed files with 69 additions and 34 deletions.
43 changes: 30 additions & 13 deletions src/ansiblelint/rules/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations

import logging
import re
import sys
from typing import TYPE_CHECKING, Any

Expand Down Expand Up @@ -193,25 +194,27 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
pytest.param(
"examples/.collection/galaxy.yml",
"galaxy",
["'GPL' is not one of"],
[r"^'GPL' is not one of.*https://"],
id="galaxy",
),
pytest.param(
"examples/roles/invalid_requirements_schema/meta/requirements.yml",
"requirements",
["{'foo': 'bar'} is not valid under any of the given schemas"],
[
r"^{'foo': 'bar'} is not valid under any of the given schemas.*https://",
],
id="requirements",
),
pytest.param(
"examples/roles/invalid_meta_schema/meta/main.yml",
"meta",
["False is not of type 'string'"],
[r"^False is not of type 'string'.*https://"],
id="meta",
),
pytest.param(
"examples/playbooks/vars/invalid_vars_schema.yml",
"vars",
["'123' does not match any of the regexes"],
[r"^'123' does not match any of the regexes.*https://"],
id="vars",
),
pytest.param(
Expand All @@ -223,14 +226,18 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
pytest.param(
"examples/ee_broken/execution-environment.yml",
"execution-environment",
["{'foo': 'bar'} is not valid under any of the given schemas"],
[
r"^{'foo': 'bar'} is not valid under any of the given schemas.*https://",
],
id="execution-environment-broken",
),
("examples/meta/runtime.yml", "meta-runtime", []),
pytest.param(
"examples/broken_collection_meta_runtime/meta/runtime.yml",
"meta-runtime",
["Additional properties are not allowed ('foo' was unexpected)"],
[
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
],
id="meta-runtime-broken",
),
pytest.param(
Expand All @@ -242,7 +249,9 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
pytest.param(
"examples/inventory/broken_dev_inventory.yml",
"inventory",
["Additional properties are not allowed ('foo' was unexpected)"],
[
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
],
id="inventory-broken",
),
pytest.param(
Expand All @@ -260,7 +269,9 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
pytest.param(
"examples/broken/.ansible-lint",
"ansible-lint-config",
["Additional properties are not allowed ('foo' was unexpected)"],
[
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
],
id="ansible-lint-config-broken",
),
pytest.param(
Expand All @@ -272,7 +283,9 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
pytest.param(
"examples/broken/ansible-navigator.yml",
"ansible-navigator-config",
["Additional properties are not allowed ('ansible' was unexpected)"],
[
r"^Additional properties are not allowed \('ansible' was unexpected\).*https://",
],
id="ansible-navigator-config-broken",
),
pytest.param(
Expand All @@ -284,20 +297,24 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
pytest.param(
"examples/roles/broken_argument_specs/meta/argument_specs.yml",
"role-arg-spec",
["Additional properties are not allowed ('foo' was unexpected)"],
[
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
],
id="role-arg-spec-broken",
),
pytest.param(
"examples/changelogs/changelog.yaml",
"changelog",
["Additional properties are not allowed ('foo' was unexpected)"],
[
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
],
id="changelog",
),
pytest.param(
"examples/rulebooks/rulebook-fail.yml",
"rulebook",
[
"Additional properties are not allowed ('that_should_not_be_here' was unexpected)",
r"^Additional properties are not allowed \('that_should_not_be_here' was unexpected\).*https://",
],
id="rulebook",
),
Expand Down Expand Up @@ -336,7 +353,7 @@ def test_schema(file: str, expected_kind: str, expected: list[str]) -> None:
assert len(results) == len(expected), results
for idx, result in enumerate(results):
assert result.filename.endswith(file)
assert expected[idx] in result.message
assert re.match(expected[idx], result.message)
assert result.tag == f"schema[{expected_kind}]"

@pytest.mark.parametrize(
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/ansible-lint-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible-lint-config.json",
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"documentation_url": "https://ansible.readthedocs.io/projects/lint/configuring/",
"examples": [
".ansible-lint",
".config/ansible-lint.yml",
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/ansible.json
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible.json",
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"documentation_url": "https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html",
"examples": [],
"title": "Ansible Schemas Bundle 22.4",
"type": ["array", "object"]
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/changelog.json
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/changelog.json",
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"documentation_url": "https://github.com/ansible-community/antsibull-changelog/blob/main/docs/changelog.yaml-format.md",
"examples": ["changelogs/changelog.yaml"],
"markdownDescription": "Antsibull Changelog Schema is based on [changelog.yaml-format.md](https://github.com/ansible-community/antsibull-changelog/blob/main/docs/changelog.yaml-format.md).",
"properties": {
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/execution-environment.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/execution-environment.json",
"$schema": "http://json-schema.org/draft-07/schema",
"description": "See \nV1: https://docs.ansible.com/automation-controller/latest/html/userguide/ee_reference.html\nV3: https://ansible-builder.readthedocs.io/en/latest/definition/",
"documentation_url": "https://ansible.readthedocs.io/projects/builder/en/latest/definition/",
"examples": ["execution-environment.yml"],
"oneOf": [{ "$ref": "#/$defs/v3" }, { "$ref": "#/$defs/v1" }],
"title": "Ansible Execution Environment Schema v1/v3"
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/galaxy.json
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/galaxy.json",
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"documentation_url": "https://docs.ansible.com/ansible/latest/dev_guide/collections_galaxy_meta.html",
"examples": ["galaxy.yml"],
"properties": {
"authors": {
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": true,
"description": "Ansible Inventory Schema",
"documentation_url": "https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html",
"examples": [
"inventory.yaml",
"inventory.yml",
Expand Down
12 changes: 10 additions & 2 deletions src/ansiblelint/schemas/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,26 @@

def validate_file_schema(file: Lintable) -> list[str]:
"""Return list of JSON validation errors found."""
schema = {}
if file.kind not in JSON_SCHEMAS:
return [f"Unable to find JSON Schema '{file.kind}' for '{file.path}' file."]
try:
# convert yaml to json (keys are converted to strings)
yaml_data = yaml_load_safe(file.content)
json_data = json.loads(json.dumps(yaml_data))
schema = _schema_cache[file.kind]
jsonschema.validate(
instance=json_data,
schema=_schema_cache[file.kind],
schema=schema,
)
except yaml.constructor.ConstructorError as exc:
return [f"Failed to load YAML file '{file.path}': {exc.problem}"]
except ValidationError as exc:
return [exc.message]
message = exc.message
# if schema has a documentation_url key at root level, we append this to the message
if "documentation_url" in schema:
if not message.endswith("."):
message += "."
message += f" See {schema['documentation_url']}"
return [message]
return []
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/meta-runtime.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"description": "See https://docs.ansible.com/ansible/devel/dev_guide/developing_collections_structure.html#meta-directory",
"documentation_url": "https://docs.ansible.com/ansible/devel/dev_guide/developing_collections_structure.html#meta-directory",
"examples": ["**/meta/runtime.yml"],
"properties": {
"action_groups": {
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,7 @@
},
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/meta.json",
"$schema": "http://json-schema.org/draft-07/schema",
"documentation_url": "https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#using-role-dependencies",
"examples": ["meta/main.yml"],
"properties": {
"additionalProperties": false,
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/molecule.json
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/molecule.json",
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"documentation_url": "https://ansible.readthedocs.io/projects/molecule/configuration/",
"examples": ["molecule/*/molecule.yml"],
"properties": {
"dependency": {
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/requirements.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"$ref": "#/$defs/RequirementsV2Model"
}
],
"documentation_url": "https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-roles-and-collections-from-the-same-requirements-yml-file",
"examples": ["requirements.yml"],
"title": "Ansible Requirements Schema"
}
24 changes: 5 additions & 19 deletions src/ansiblelint/schemas/role-arg-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,26 +148,11 @@
},
"option": {
"additionalProperties": false,
"aliases": {
"items": {
"markdownDescription": "See [argument-spec](https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec)",
"properties": {
"apply_defaults": {
"type": "string"
},
"type": "array"
},
"apply_defaults": {
"type": "string"
},
"deprecated_aliases": {
"items": {
"$ref": "#/$defs/deprecated_alias"
},
"type": "array"
},
"markdownDescription": "xxx",
"options": {
"$ref": "#/$defs/option"
},
"properties": {
"choices": {
"type": "array"
},
Expand Down Expand Up @@ -236,8 +221,9 @@
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/role-arg-spec.json",
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"documentation_url": "https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-argument-validation",
"examples": ["meta/argument_specs.yml"],
"markdownDescription": "Add entry point, usually `main`.\nSee [role-argument-validation](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-argument-validation)",
"markdownDescription": "XSee [role-argument-validation](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-argument-validation)",
"properties": {
"argument_specs": {
"additionalProperties": {
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/vars.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"type": "null"
}
],
"documentation_url": "https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html",
"examples": [
"playbooks/vars/*.yml",
"vars/*.yml",
Expand Down
13 changes: 13 additions & 0 deletions test/schemas/test/roles/foo/meta/argument_specs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ argument_specs:
description: "The integer value, defaulting to 42."
no_log: false
version_added: 1.0.0
aliases: ["my_app_integer"]
deprecated_aliases:
[
{
"name": "foo",
"version": "2.0.0",
"collection_name": "acme.goodies",
},
]
mutually_exclusive:
- "foo"
- "bar"

my_app_str:
type: "str"
Expand All @@ -25,6 +37,7 @@ argument_specs:
- foo
- bar
- baz
mutually_exclusive: [["foo", "bar"]]

top_level:
type: dict
Expand Down

0 comments on commit aa1efdb

Please sign in to comment.