From 0d89284789dbb9c7dfad3a4b2951fae6ba6376d5 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Fri, 13 May 2022 15:10:21 +0100 Subject: [PATCH] schema: add validation of argument_specs.yml files (#2131) Fixes: #1966 --- .github/workflows/tox.yml | 2 +- .../meta/argument_specs.yml | 5 + examples/roles/hello/meta/argument_specs.yml | 27 ++++ src/ansiblelint/config.py | 1 + src/ansiblelint/rules/schema.py | 12 ++ src/ansiblelint/schemas/arg_specs.json | 143 ++++++++++++++++++ 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 examples/roles/broken_argument_specs/meta/argument_specs.yml create mode 100644 examples/roles/hello/meta/argument_specs.yml create mode 100644 src/ansiblelint/schemas/arg_specs.json diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 0664fd7d7a..0dad9ee075 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -155,7 +155,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:TOX_PARALLEL_NO_SPINNER # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 642 + PYTEST_REQPASS: 644 steps: - name: Activate WSL1 diff --git a/examples/roles/broken_argument_specs/meta/argument_specs.yml b/examples/roles/broken_argument_specs/meta/argument_specs.yml new file mode 100644 index 0000000000..ddc986261f --- /dev/null +++ b/examples/roles/broken_argument_specs/meta/argument_specs.yml @@ -0,0 +1,5 @@ +--- +argument_specs: + main: + foo: bar # <-- invalid based on json schema + options: {} diff --git a/examples/roles/hello/meta/argument_specs.yml b/examples/roles/hello/meta/argument_specs.yml new file mode 100644 index 0000000000..47bde78971 --- /dev/null +++ b/examples/roles/hello/meta/argument_specs.yml @@ -0,0 +1,27 @@ +--- +# https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-argument-validation +argument_specs: + main: + short_description: The main entry point for the role. + description: "a longer description" + options: + my_app_int: + type: "int" + required: false + default: 42 + description: "The integer value, defaulting to 42." + no_log: false + + my_app_str: + type: "str" + required: true + description: "The string value" + + alternate: + short_description: The alternate entry point for the my_app role. + options: + my_app_int: + type: "int" + required: false + default: 1024 + description: "The integer value, defaulting to 1024." diff --git a/src/ansiblelint/config.py b/src/ansiblelint/config.py index 05cc54bfef..46d6085c89 100644 --- a/src/ansiblelint/config.py +++ b/src/ansiblelint/config.py @@ -77,6 +77,7 @@ "inventory": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-inventory.json", "ansible-lint-config": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-lint.json", "ansible-navigator-config": "https://raw.githubusercontent.com/ansible/ansible-navigator/main/src/ansible_navigator/data/ansible-navigator.json", + "arg_specs": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-argument-specs.json", } options = Namespace( diff --git a/src/ansiblelint/rules/schema.py b/src/ansiblelint/rules/schema.py index 6abb566d82..eb6104794a 100644 --- a/src/ansiblelint/rules/schema.py +++ b/src/ansiblelint/rules/schema.py @@ -166,6 +166,16 @@ def matchyaml(self, file: Lintable) -> List[MatchError]: "ansible-navigator-config", ["Additional properties are not allowed ('ansible' was unexpected)"], ), + ( + "examples/roles/hello/meta/argument_specs.yml", + "arg_specs", + [], + ), + ( + "examples/roles/broken_argument_specs/meta/argument_specs.yml", + "arg_specs", + ["Additional properties are not allowed ('foo' was unexpected)"], + ), ), ids=( # "playbook-fail", @@ -184,6 +194,8 @@ def matchyaml(self, file: Lintable) -> List[MatchError]: "lint-config-broken", "navigator", "navigator-broken", + "argspecs", + "argspecs-broken", ), ) def test_schema(file: str, expected_kind: str, expected: List[str]) -> None: diff --git a/src/ansiblelint/schemas/arg_specs.json b/src/ansiblelint/schemas/arg_specs.json new file mode 100644 index 0000000000..75c8e8d83f --- /dev/null +++ b/src/ansiblelint/schemas/arg_specs.json @@ -0,0 +1,143 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-argument-specs.json", + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "definitions": { + "datatype": { + "enum": [ + "str", + "list", + "dict", + "bool", + "int", + "float", + "path", + "raw", + "jsonarg", + "json", + "bytes", + "bits" + ], + "type": "string" + }, + "deprecated_alias": { + "properties": { + "collection_name": { + "type": "string" + }, + "date": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": ["name"], + "type": "object" + }, + "entry_point": { + "additionalProperties": false, + "properties": { + "author": { + "type": "string" + }, + "description": { + "type": "string" + }, + "options": { + "additionalProperties": { + "$ref": "#/definitions/option" + }, + "type": "object" + }, + "short_description": { + "type": "string" + } + }, + "required": ["options"], + "title": "Entry Point", + "type": "object" + }, + "option": { + "additionalProperties": false, + "aliases": { + "items": { + "type": "string" + }, + "type": "array" + }, + "apply_defaults": { + "type": "string" + }, + "deprecated_aliases": { + "items": { + "$ref": "#/definitions/deprecated_alias" + }, + "type": "array" + }, + "markdownDescription": "xxx", + "options": { + "$ref": "#/definitions/option" + }, + "properties": { + "choices": { + "$ref": "#/definitions/datatype" + }, + "default": { + "default": "None" + }, + "description": { + "description": "Detailed explanation of what this option does. It should be written in full sentences.", + "type": "string" + }, + "elements": { + "$ref": "#/definitions/datatype" + }, + "fallback": { + "default": "None", + "type": "string" + }, + "no_log": { + "default": false, + "type": "boolean" + }, + "option-name": { + "description": "The name of the option/argument.", + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "type": { + "$ref": "#/definitions/datatype", + "markdownDescription": "See [argument-spec](https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec" + } + }, + "removed_at_date": { + "type": "string" + }, + "removed_from_collection": { + "type": "string" + }, + "removed_in_version": { + "type": "string" + }, + "title": "Option" + } + }, + "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)", + "properties": { + "argument_specs": { + "additionalProperties": { + "$ref": "#/definitions/entry_point" + }, + "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)" + } + }, + "title": "Ansible Argument Specs Schema" +}