Skip to content

Commit 9d96cfb

Browse files
authored
Include links to documentation for schema validation errors (#3684)
1 parent 917b3bc commit 9d96cfb

15 files changed

+67
-38
lines changed

.readthedocs.yml

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ build:
1313
- pip install --user tox
1414
- python3 -m tox -e docs -- --strict --site-dir=_readthedocs/html/
1515
python:
16-
system_packages: false
1716
install:
1817
- method: pip
1918
path: tox

src/ansiblelint/rules/schema.py

+30-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import annotations
33

44
import logging
5+
import re
56
import sys
67
from typing import TYPE_CHECKING, Any
78

@@ -193,25 +194,27 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
193194
pytest.param(
194195
"examples/.collection/galaxy.yml",
195196
"galaxy",
196-
["'GPL' is not one of"],
197+
[r"^'GPL' is not one of.*https://"],
197198
id="galaxy",
198199
),
199200
pytest.param(
200201
"examples/roles/invalid_requirements_schema/meta/requirements.yml",
201202
"requirements",
202-
["{'foo': 'bar'} is not valid under any of the given schemas"],
203+
[
204+
r"^{'foo': 'bar'} is not valid under any of the given schemas.*https://",
205+
],
203206
id="requirements",
204207
),
205208
pytest.param(
206209
"examples/roles/invalid_meta_schema/meta/main.yml",
207210
"meta",
208-
["False is not of type 'string'"],
211+
[r"^False is not of type 'string'.*https://"],
209212
id="meta",
210213
),
211214
pytest.param(
212215
"examples/playbooks/vars/invalid_vars_schema.yml",
213216
"vars",
214-
["'123' does not match any of the regexes"],
217+
[r"^'123' does not match any of the regexes.*https://"],
215218
id="vars",
216219
),
217220
pytest.param(
@@ -223,14 +226,18 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
223226
pytest.param(
224227
"examples/ee_broken/execution-environment.yml",
225228
"execution-environment",
226-
["{'foo': 'bar'} is not valid under any of the given schemas"],
229+
[
230+
r"^{'foo': 'bar'} is not valid under any of the given schemas.*https://",
231+
],
227232
id="execution-environment-broken",
228233
),
229234
("examples/meta/runtime.yml", "meta-runtime", []),
230235
pytest.param(
231236
"examples/broken_collection_meta_runtime/meta/runtime.yml",
232237
"meta-runtime",
233-
["Additional properties are not allowed ('foo' was unexpected)"],
238+
[
239+
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
240+
],
234241
id="meta-runtime-broken",
235242
),
236243
pytest.param(
@@ -242,7 +249,9 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
242249
pytest.param(
243250
"examples/inventory/broken_dev_inventory.yml",
244251
"inventory",
245-
["Additional properties are not allowed ('foo' was unexpected)"],
252+
[
253+
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
254+
],
246255
id="inventory-broken",
247256
),
248257
pytest.param(
@@ -260,7 +269,9 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
260269
pytest.param(
261270
"examples/broken/.ansible-lint",
262271
"ansible-lint-config",
263-
["Additional properties are not allowed ('foo' was unexpected)"],
272+
[
273+
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
274+
],
264275
id="ansible-lint-config-broken",
265276
),
266277
pytest.param(
@@ -272,7 +283,9 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
272283
pytest.param(
273284
"examples/broken/ansible-navigator.yml",
274285
"ansible-navigator-config",
275-
["Additional properties are not allowed ('ansible' was unexpected)"],
286+
[
287+
r"^Additional properties are not allowed \('ansible' was unexpected\).*https://",
288+
],
276289
id="ansible-navigator-config-broken",
277290
),
278291
pytest.param(
@@ -284,20 +297,24 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
284297
pytest.param(
285298
"examples/roles/broken_argument_specs/meta/argument_specs.yml",
286299
"role-arg-spec",
287-
["Additional properties are not allowed ('foo' was unexpected)"],
300+
[
301+
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
302+
],
288303
id="role-arg-spec-broken",
289304
),
290305
pytest.param(
291306
"examples/changelogs/changelog.yaml",
292307
"changelog",
293-
["Additional properties are not allowed ('foo' was unexpected)"],
308+
[
309+
r"^Additional properties are not allowed \('foo' was unexpected\).*https://",
310+
],
294311
id="changelog",
295312
),
296313
pytest.param(
297314
"examples/rulebooks/rulebook-fail.yml",
298315
"rulebook",
299316
[
300-
"Additional properties are not allowed ('that_should_not_be_here' was unexpected)",
317+
r"^Additional properties are not allowed \('that_should_not_be_here' was unexpected\).*https://",
301318
],
302319
id="rulebook",
303320
),
@@ -336,7 +353,7 @@ def test_schema(file: str, expected_kind: str, expected: list[str]) -> None:
336353
assert len(results) == len(expected), results
337354
for idx, result in enumerate(results):
338355
assert result.filename.endswith(file)
339-
assert expected[idx] in result.message
356+
assert re.match(expected[idx], result.message)
340357
assert result.tag == f"schema[{expected_kind}]"
341358

342359
@pytest.mark.parametrize(

src/ansiblelint/schemas/__store__.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible-lint-config.json"
55
},
66
"ansible-navigator-config": {
7-
"etag": "dd0f0dea68266ae61e5a8d6aed0a1279fdee16f2da4911bc27970241df80f798",
7+
"etag": "815582d2c4b252906bf83d1da9667dedf16dbd9b6149cfceea205c4848c78f53",
88
"url": "https://raw.githubusercontent.com/ansible/ansible-navigator/main/src/ansible_navigator/data/ansible-navigator.json"
99
},
1010
"changelog": {

src/ansiblelint/schemas/ansible-lint-config.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible-lint-config.json",
1818
"$schema": "http://json-schema.org/draft-07/schema",
1919
"additionalProperties": false,
20+
"description": "https://ansible.readthedocs.io/projects/lint/configuring/",
2021
"examples": [
2122
".ansible-lint",
2223
".config/ansible-lint.yml",

src/ansiblelint/schemas/ansible-navigator-config.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"$schema": "http://json-schema.org/draft-07/schema",
33
"additionalProperties": false,
4+
"description": "See https://ansible.readthedocs.io/projects/navigator/settings/",
45
"properties": {
56
"ansible-navigator": {
67
"additionalProperties": false,

src/ansiblelint/schemas/ansible.json

+1
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,7 @@
11961196
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible.json",
11971197
"$schema": "http://json-schema.org/draft-07/schema",
11981198
"additionalProperties": false,
1199+
"description": "https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html",
11991200
"examples": [],
12001201
"title": "Ansible Schemas Bundle 22.4",
12011202
"type": ["array", "object"]

src/ansiblelint/schemas/execution-environment.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@
302302
},
303303
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/execution-environment.json",
304304
"$schema": "http://json-schema.org/draft-07/schema",
305-
"description": "See \nV1: https://docs.ansible.com/automation-controller/latest/html/userguide/ee_reference.html\nV3: https://ansible-builder.readthedocs.io/en/latest/definition/",
305+
"description": "See https://ansible-builder.readthedocs.io/en/latest/definition/ for V3 or https://docs.ansible.com/automation-controller/latest/html/userguide/ee_reference.html for older V1 format.\n",
306+
"documentation_url": "https://ansible.readthedocs.io/projects/builder/en/latest/definition/",
306307
"examples": ["execution-environment.yml"],
307308
"oneOf": [{ "$ref": "#/$defs/v3" }, { "$ref": "#/$defs/v1" }],
308309
"title": "Ansible Execution Environment Schema v1/v3"

src/ansiblelint/schemas/galaxy.json

+1
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@
525525
"$schema": "http://json-schema.org/draft-07/schema",
526526
"additionalProperties": false,
527527
"examples": ["galaxy.yml"],
528+
"markdownDescription": "https://docs.ansible.com/ansible/latest/dev_guide/collections_galaxy_meta.html",
528529
"properties": {
529530
"authors": {
530531
"items": {

src/ansiblelint/schemas/inventory.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/inventory.json",
4646
"$schema": "http://json-schema.org/draft-07/schema",
4747
"additionalProperties": true,
48-
"description": "Ansible Inventory Schema",
48+
"description": "See https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html",
4949
"examples": [
5050
"inventory.yaml",
5151
"inventory.yml",

src/ansiblelint/schemas/main.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import json
55
import logging
6+
import re
67
from typing import TYPE_CHECKING
78

89
import jsonschema
@@ -20,18 +21,36 @@
2021

2122
def validate_file_schema(file: Lintable) -> list[str]:
2223
"""Return list of JSON validation errors found."""
24+
schema = {}
2325
if file.kind not in JSON_SCHEMAS:
2426
return [f"Unable to find JSON Schema '{file.kind}' for '{file.path}' file."]
2527
try:
2628
# convert yaml to json (keys are converted to strings)
2729
yaml_data = yaml_load_safe(file.content)
2830
json_data = json.loads(json.dumps(yaml_data))
31+
schema = _schema_cache[file.kind]
2932
jsonschema.validate(
3033
instance=json_data,
31-
schema=_schema_cache[file.kind],
34+
schema=schema,
3235
)
3336
except yaml.constructor.ConstructorError as exc:
3437
return [f"Failed to load YAML file '{file.path}': {exc.problem}"]
3538
except ValidationError as exc:
36-
return [exc.message]
39+
message = exc.message
40+
documentation_url = ""
41+
for k in ("description", "markdownDescription"):
42+
if k in schema:
43+
# Find standalone URLs and also markdown urls.
44+
match = re.search(
45+
r"\[.*?\]\((https?://[^\s]+)\)|https?://[^\s]+",
46+
schema[k],
47+
)
48+
if match:
49+
documentation_url = match[0] if match[0] else match[1]
50+
break
51+
if documentation_url:
52+
if not message.endswith("."):
53+
message += "."
54+
message += f" See {documentation_url}"
55+
return [message]
3756
return []

src/ansiblelint/schemas/meta.json

+1
Original file line numberDiff line numberDiff line change
@@ -1447,6 +1447,7 @@
14471447
},
14481448
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/meta.json",
14491449
"$schema": "http://json-schema.org/draft-07/schema",
1450+
"description": "https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#using-role-dependencies",
14501451
"examples": ["meta/main.yml"],
14511452
"properties": {
14521453
"additionalProperties": false,

src/ansiblelint/schemas/molecule.json

+1
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@
512512
"$id": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/molecule.json",
513513
"$schema": "http://json-schema.org/draft-07/schema",
514514
"additionalProperties": false,
515+
"description": "https://ansible.readthedocs.io/projects/molecule/configuration/",
515516
"examples": ["molecule/*/molecule.yml"],
516517
"properties": {
517518
"dependency": {

src/ansiblelint/schemas/requirements.json

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
"$ref": "#/$defs/RequirementsV2Model"
131131
}
132132
],
133+
"description": "https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-roles-and-collections-from-the-same-requirements-yml-file",
133134
"examples": ["requirements.yml"],
134135
"title": "Ansible Requirements Schema"
135136
}

src/ansiblelint/schemas/role-arg-spec.json

+4-19
Original file line numberDiff line numberDiff line change
@@ -148,26 +148,11 @@
148148
},
149149
"option": {
150150
"additionalProperties": false,
151-
"aliases": {
152-
"items": {
151+
"markdownDescription": "See [argument-spec](https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec)",
152+
"properties": {
153+
"apply_defaults": {
153154
"type": "string"
154155
},
155-
"type": "array"
156-
},
157-
"apply_defaults": {
158-
"type": "string"
159-
},
160-
"deprecated_aliases": {
161-
"items": {
162-
"$ref": "#/$defs/deprecated_alias"
163-
},
164-
"type": "array"
165-
},
166-
"markdownDescription": "xxx",
167-
"options": {
168-
"$ref": "#/$defs/option"
169-
},
170-
"properties": {
171156
"choices": {
172157
"type": "array"
173158
},
@@ -237,7 +222,7 @@
237222
"$schema": "http://json-schema.org/draft-07/schema",
238223
"additionalProperties": false,
239224
"examples": ["meta/argument_specs.yml"],
240-
"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)",
225+
"markdownDescription": "See [role-argument-validation](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-argument-validation)",
241226
"properties": {
242227
"argument_specs": {
243228
"additionalProperties": {

src/ansiblelint/schemas/vars.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"type": "null"
1818
}
1919
],
20+
"description": "https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html",
2021
"examples": [
2122
"playbooks/vars/*.yml",
2223
"vars/*.yml",

0 commit comments

Comments
 (0)