diff --git a/.config/dictionary.txt b/.config/dictionary.txt index f7539da619..1466891c6b 100644 --- a/.config/dictionary.txt +++ b/.config/dictionary.txt @@ -76,6 +76,7 @@ isdir isdisjoint iskeyword isort +jsonschema junitxml kubernetes libera diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 90a6407e13..2f7bc10924 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: 625 + PYTEST_REQPASS: 629 steps: - name: Activate WSL1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8f10c24fc..30141b0973 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,7 +28,8 @@ repos: examples/playbooks/templates/not-valid.yaml| examples/playbooks/with-umlaut-.*| examples/playbooks/with-skip-tag-id.yml| - test/fixtures/formatting-before/.* + test/fixtures/formatting-before/.*| + src/ansiblelint/schemas/.* )$ additional_dependencies: - prettier @@ -60,6 +61,7 @@ repos: (?x)^( examples/playbooks/(with-skip-tag-id|unicode).yml| examples/playbooks/example.yml| + test/eco/.*.result| test/fixtures/formatting-before/.* )$ - id: mixed-line-ending @@ -72,6 +74,10 @@ repos: rev: v2.1.0 hooks: - id: codespell + exclude: > + (?x)^( + src/ansiblelint/schemas/.*\.json + )$ - repo: https://github.com/PyCQA/doc8 rev: 0.11.1 hooks: @@ -126,10 +132,11 @@ repos: - rich>=11.0.0 - ruamel.yaml - sphinx>=4.4.0 - - types-pyyaml>=6.0.4 - types-dataclasses - types-docutils + - types-jsonschema>=4.4.2 - types-pkg_resources + - types-pyyaml>=6.0.4 - wcmatch - yamllint exclude: > @@ -147,6 +154,7 @@ repos: - docutils - enrich - flaky + - jsonschema>=4.5.1 - pytest - pyyaml - rich>=11.0.0 diff --git a/conftest.py b/conftest.py index 45570e1094..a294ea72cc 100644 --- a/conftest.py +++ b/conftest.py @@ -2,6 +2,9 @@ import importlib import os import sys +from typing import Any + +from ansiblelint.schemas import refresh_schemas # checking if user is running pytest without installing test dependencies: missing = [] @@ -18,3 +21,10 @@ os.environ["NO_COLOR"] = "1" pytest_plugins = ["ansiblelint.testing.fixtures"] + + +def pytest_configure(config: Any) -> None: + """Configure pytest.""" + # run only on master node (xdist): + if not hasattr(config, "slaveinput"): + refresh_schemas() diff --git a/cspell.config.yaml b/cspell.config.yaml index 44b4504472..b6ed031ea3 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -17,6 +17,7 @@ ignorePaths: - docs/requirements.in # Test fixtures generated from outside - test/**/*.result + - src/ansiblelint/schemas/*.json # Other - "*.svg" allowCompoundWords: true diff --git a/examples/galaxy.yml b/examples/galaxy.yml index 10845f2be4..f5cda0a9e8 100644 --- a/examples/galaxy.yml +++ b/examples/galaxy.yml @@ -11,5 +11,5 @@ dependencies: other_namespace.collection2: ">=2.0.0,<3.0.0" anderson55.my_collection: "*" # note: "*" selects the highest version available license: - - GPL + - GPL # <-- invalid license values based on galaxy schema - Apache diff --git a/examples/playbooks/command-check-failure.yml b/examples/playbooks/command-check-failure.yml index 630f2e951e..92b51e0615 100644 --- a/examples/playbooks/command-check-failure.yml +++ b/examples/playbooks/command-check-failure.yml @@ -10,3 +10,4 @@ ansible.builtin.shell: echo blah args: chdir: X + become_method: xx diff --git a/examples/playbooks/json-schema-fail.yml b/examples/playbooks/json-schema-fail.yml new file mode 100644 index 0000000000..5760632982 --- /dev/null +++ b/examples/playbooks/json-schema-fail.yml @@ -0,0 +1,4 @@ +--- +- name: This should raise json-schema error, due to hosts missing the last letter + host: localhost + tasks: [] diff --git a/examples/playbooks/schema-error-string.yml b/examples/playbooks/schema-error-string.yml new file mode 100644 index 0000000000..00e5e408bd --- /dev/null +++ b/examples/playbooks/schema-error-string.yml @@ -0,0 +1,4 @@ +--- +foo +# This file is valid YAML but from our point of view is an error, as is +# neither a Sequence or a Mapping. diff --git a/examples/playbooks/syntax-error-string.yml b/examples/playbooks/syntax-error-string.yml index fc1c31a136..ebb00f2395 100644 --- a/examples/playbooks/syntax-error-string.yml +++ b/examples/playbooks/syntax-error-string.yml @@ -1,3 +1,7 @@ -foo -# This file is valid YAML but from our point of view is an error, as is -# neither a Sequence or a Mapping. +# This file is valid YAML and passed JSON Schema validation but not ansible +# own syntax check. + +- hosts: localhost + tasks: + - name: invalid syntax + x.y.z.w: {} diff --git a/examples/playbooks/vars/invalid_vars_schema.yml b/examples/playbooks/vars/invalid_vars_schema.yml new file mode 100644 index 0000000000..d4142a26d9 --- /dev/null +++ b/examples/playbooks/vars/invalid_vars_schema.yml @@ -0,0 +1,2 @@ +--- +123: true # invalid as schema expects string key diff --git a/examples/roles/dependency_in_meta/meta/main.yml b/examples/roles/dependency_in_meta/meta/main.yml index c49cef7a13..113076bb63 100644 --- a/examples/roles/dependency_in_meta/meta/main.yml +++ b/examples/roles/dependency_in_meta/meta/main.yml @@ -1,6 +1,7 @@ --- # meta file, determined by ending in meta/main.yml # https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-dependencies +allow_duplicates: true dependencies: # from Bitbucket - src: git+http://bitbucket.org/willthames/git-ansible-galaxy @@ -35,6 +36,6 @@ galaxy_info: description: Testing meta company: Not applicable license: MIT - min_ansible_version: 2.5 + min_ansible_version: "2.5" platforms: - name: Fedora diff --git a/examples/roles/invalid_due_to_meta/meta/main.yml b/examples/roles/invalid_due_to_meta/meta/main.yml index 9d3f391ef5..cda87b223c 100644 --- a/examples/roles/invalid_due_to_meta/meta/main.yml +++ b/examples/roles/invalid_due_to_meta/meta/main.yml @@ -1,9 +1,9 @@ --- galaxy_info: - role_name: invalid-due-to-meta + role_name: invalid-due-to-meta # <-- invalid role name author: foo description: foo license: MIT platforms: - - name: foo - min_ansible_version: 2.7 + - name: AIX + min_ansible_version: "2.7" diff --git a/examples/roles/invalid_meta_schema/meta/main.yml b/examples/roles/invalid_meta_schema/meta/main.yml new file mode 100644 index 0000000000..4d948ac918 --- /dev/null +++ b/examples/roles/invalid_meta_schema/meta/main.yml @@ -0,0 +1,8 @@ +--- +galaxy_info: + author: foo + description: false # <-- schema fail as string is expected + license: XXX + platforms: + - name: AIX + min_ansible_version: "2.7" diff --git a/examples/roles/invalid_requirements_schema/meta/requirements.yml b/examples/roles/invalid_requirements_schema/meta/requirements.yml new file mode 100644 index 0000000000..41a70afb39 --- /dev/null +++ b/examples/roles/invalid_requirements_schema/meta/requirements.yml @@ -0,0 +1,3 @@ +--- +# this should fail validation +foo: bar diff --git a/examples/roles/invalud_meta_schema b/examples/roles/invalud_meta_schema new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/roles/valid-due-to-meta/meta/main.yml b/examples/roles/valid-due-to-meta/meta/main.yml index 71c66d9445..ae7c2b8d31 100644 --- a/examples/roles/valid-due-to-meta/meta/main.yml +++ b/examples/roles/valid-due-to-meta/meta/main.yml @@ -5,5 +5,5 @@ galaxy_info: description: foo license: MIT platforms: - - name: foo - min_ansible_version: 2.7 + - name: Fedora + min_ansible_version: "2.7" diff --git a/requirements.txt b/requirements.txt index 3986bfbec3..8c8425b06d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,6 +32,7 @@ importlib-metadata==4.11.3 iniconfig==1.1.1 isort==5.10.1 jinja2==3.1.2 +jsonschema==4.5.1 lazy-object-proxy==1.7.1 markdown-it-py==2.1.0 markupsafe==2.1.1 @@ -56,6 +57,7 @@ pyflakes==2.4.0 pygments==2.12.0 pylint==2.13.8 pyparsing==3.0.8 +pyrsistent==0.18.1 pytest==7.1.2 pytest-cov==3.0.0 pytest-forked==1.4.0 diff --git a/setup.cfg b/setup.cfg index 07804ca492..7572456321 100644 --- a/setup.cfg +++ b/setup.cfg @@ -66,6 +66,7 @@ install_requires = ansible-compat>=2.0.2 # GPLv3 ansible-core>=2.12.0 # GPLv3 enrich>=1.2.6 + jsonschema>=4.5.1 # MIT, first version to have ordered keys in output packaging pyyaml pytest diff --git a/src/ansiblelint/config.py b/src/ansiblelint/config.py index 7966372202..673e8804c2 100644 --- a/src/ansiblelint/config.py +++ b/src/ansiblelint/config.py @@ -9,6 +9,7 @@ # Do not sort this list, order matters. {"jinja2": "**/*.j2"}, # jinja2 templates are not always parsable as something else {"jinja2": "**/*.j2.*"}, + {"yaml": ".github/**/*.{yaml,yml}"}, # github workflows {"text": "**/templates/**/*.*"}, # templates are likely not validable {"inventory": "**/inventory/**.yml"}, {"requirements": "**/meta/requirements.yml"}, # v1 only @@ -55,6 +56,25 @@ ] +# Maps kinds to JSON schemas +# See https://www.schemastore.org/json/ +JSON_SCHEMAS = { + # playbook and task schemas not used yet due jsonschema bug: + # https://github.com/python-jsonschema/jsonschema/issues/931 + # "playbook": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible.json#/definitions/playbook", + # "tasks": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible.json#/definitions/tasks", + "vars": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-vars.json", + "requirements": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-requirements.json", + "meta": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-meta.json", + "galaxy": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-galaxy.json", + # unsupported yet: + "execution-environment": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-ee.json", + "meta-runtime": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-meta-runtime.json", + "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", +} + options = Namespace( cache_dir=None, colored=True, diff --git a/src/ansiblelint/rules/risky_file_permissions.py b/src/ansiblelint/rules/risky_file_permissions.py index c6c662dda7..e41462f33e 100644 --- a/src/ansiblelint/rules/risky_file_permissions.py +++ b/src/ansiblelint/rules/risky_file_permissions.py @@ -293,11 +293,11 @@ def matchtask( FAIL_INI_PERMISSION = """ - hosts: all - tasks: - - name: permissions needed if create is used - ini_file: - path: foo - create: true + tasks: + - name: permissions needed if create is used + ini_file: + path: foo + create: true """ FAIL_INI_PRESERVE = """ diff --git a/src/ansiblelint/rules/schema.py b/src/ansiblelint/rules/schema.py new file mode 100644 index 0000000000..0a464cac4b --- /dev/null +++ b/src/ansiblelint/rules/schema.py @@ -0,0 +1,147 @@ +"""Rule definition for JSON Schema Validations.""" +import json +import logging +import os +import sys +from functools import lru_cache +from typing import Any, List + +import yaml +from jsonschema import validate +from jsonschema.exceptions import ValidationError + +from ansiblelint.config import JSON_SCHEMAS +from ansiblelint.errors import MatchError +from ansiblelint.file_utils import Lintable +from ansiblelint.rules import AnsibleLintRule +from ansiblelint.schemas import __file__ as schemas_module + +_logger = logging.getLogger(__name__) + + +class ValidateSchemaRule(AnsibleLintRule): + """Perform JSON Schema Validation for known lintable kinds. + + Returned errors will not include exact line numbers, but they will mention + the schema name being used as a tag, like ``playbook-schema``, + ``tasks-schema``. + + This rule is not skippable and stops further processing of the file. + + Schema bugs should be reported towards https://github.com/ansible/schemas + project instead of ansible-lint. + + If incorrect schema was picked, you might want to either: + + * move the file to standard location, so its file is detected correctly. + * use ``kinds:`` option in linter config to help it pick correct file type. + """ + + id = "schema" + description = __doc__ + severity = "VERY_HIGH" + tags = ["core", "experimental"] + version_added = "v6.1.0" + + @staticmethod + @lru_cache(maxsize=None) + def _get_schema(kind: str) -> Any: + """Return the schema for the given kind.""" + schema_file = os.path.dirname(schemas_module) + "/" + kind + ".json" + with open(schema_file, encoding="utf-8") as f: + return json.load(f) + + def matchyaml(self, file: Lintable) -> List[MatchError]: + """Return JSON validation errors found as a list of MatchError(s).""" + result = [] + if file.kind not in JSON_SCHEMAS: + return [] + + try: + # convert yaml to json (keys are converted to strings) + yaml_data = yaml.safe_load(file.content) + json_data = json.loads(json.dumps(yaml_data)) + validate( + instance=json_data, + schema=ValidateSchemaRule._get_schema(file.kind), + ) + except yaml.constructor.ConstructorError: + _logger.debug( + "Ignored failure to load %s for schema validation, as !vault may cause it." + ) + return [] + except ValidationError as exc: + result.append( + MatchError( + message=exc.message, + filename=file, + rule=ValidateSchemaRule(), + details=ValidateSchemaRule.description, + tag=f"schema[{file.kind}]", + ) + ) + return result + + +# testing code to be loaded only with pytest or when executed the rule file +if "pytest" in sys.modules: + import pytest + + # pylint: disable=ungrouped-imports + from ansiblelint.config import options + from ansiblelint.rules import RulesCollection + from ansiblelint.runner import Runner + + @pytest.mark.parametrize( + ("file", "expected_kind", "expected"), + ( + ( + "examples/galaxy.yml", + "galaxy", + ["'GPL' is not one of"], + ), + ( + "examples/roles/invalid_requirements_schema/meta/requirements.yml", + "requirements", + ["'collections' is a required property"], + ), + ( + "examples/roles/invalid_meta_schema/meta/main.yml", + "meta", + ["False is not of type 'string'"], + ), + ( + "examples/playbooks/vars/invalid_vars_schema.yml", + "vars", + ["'123' does not match any of the regexes"], + ), + ), + ids=( + # "playbook-fail", + "galaxy", + "requirements", + "meta", + "vars", + ), + ) + # # unsupported yet: + # "execution-environment": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-ee.json", + # "meta-runtime": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-meta-runtime.json", + # "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", + def test_schema(file: str, expected_kind: str, expected: List[str]) -> None: + """Validate parsing of ansible output.""" + lintable = Lintable(file) + assert lintable.kind == expected_kind + + rules = RulesCollection(options=options) + rules.register(ValidateSchemaRule()) + results = Runner(lintable, rules=rules).run() + + # ValidateSchemaRule.process_lintable(lintable) + assert len(results) == len(expected), results + for idx, result in enumerate(results): + assert result.filename.endswith(file) + assert expected[idx] in result.message + assert result.tag == f"schema[{expected_kind}]" diff --git a/src/ansiblelint/schemas/__init__.py b/src/ansiblelint/schemas/__init__.py new file mode 100644 index 0000000000..93db9108a9 --- /dev/null +++ b/src/ansiblelint/schemas/__init__.py @@ -0,0 +1,19 @@ +"""Module containing cached JSON schemas.""" +import os +import urllib.request + +from ansiblelint.config import JSON_SCHEMAS + + +def refresh_schemas() -> None: + """Refresh JSON schemas by downloading latest versions.""" + for kind, url in sorted(JSON_SCHEMAS.items()): + path = f"{os.path.relpath(os.path.dirname(__file__))}/{kind}.json" + print(f"Refreshing {path} ...") + with urllib.request.urlopen(url) as response: + content = response.read().decode("utf-8") + with open(f"{path}", "r+", encoding="utf-8") as f_out: + if f_out.read() != content: + f_out.seek(0) + f_out.write(content) + f_out.truncate() diff --git a/src/ansiblelint/schemas/ansible-lint-config.json b/src/ansiblelint/schemas/ansible-lint-config.json new file mode 100644 index 0000000000..47ec65a8a1 --- /dev/null +++ b/src/ansiblelint/schemas/ansible-lint-config.json @@ -0,0 +1,114 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-lint.json", + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "examples": [".ansible-lint", ".config/ansible-lint.yml"], + "properties": { + "enable_list": { + "items": { + "type": "string" + }, + "title": "Enable List", + "type": "array" + }, + "exclude_paths": { + "items": { + "type": "string" + }, + "title": "Exclude Paths", + "type": "array" + }, + "extra_vars": { + "title": "Extra Vars", + "type": "object" + }, + "kinds": { + "items": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "title": "Kinds", + "type": "array" + }, + "loop_var_prefix": { + "title": "Loop Var Prefix", + "type": "string" + }, + "mock_modules": { + "items": { + "type": "string" + }, + "title": "Mock Modules", + "type": "array" + }, + "mock_roles": { + "items": { + "type": "string" + }, + "title": "Mock Roles", + "type": "array" + }, + "offline": { + "default": false, + "title": "Offline", + "type": "boolean" + }, + "parseable": { + "default": true, + "title": "Parseable", + "type": "boolean" + }, + "quiet": { + "default": true, + "title": "Quiet", + "type": "boolean" + }, + "rulesdir": { + "items": { + "type": "string" + }, + "title": "Rulesdir", + "type": "array" + }, + "skip_action_validation": { + "default": false, + "title": "Skip Action Validation", + "type": "boolean" + }, + "skip_list": { + "items": { + "type": "string" + }, + "title": "Skip List", + "type": "array" + }, + "tags": { + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "use_default_rules": { + "default": true, + "title": "Use Default Rules", + "type": "boolean" + }, + "verbosity": { + "default": 0, + "title": "Verbosity", + "type": "integer" + }, + "warn_list": { + "items": { + "type": "string" + }, + "title": "Warn List", + "type": "array" + } + }, + "title": "Ansible-lint Configuration Schema", + "type": "object" +} diff --git a/src/ansiblelint/schemas/ansible-navigator-config.json b/src/ansiblelint/schemas/ansible-navigator-config.json new file mode 100644 index 0000000000..ffffa3281e --- /dev/null +++ b/src/ansiblelint/schemas/ansible-navigator-config.json @@ -0,0 +1,500 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "properties": { + "ansible-navigator": { + "additionalProperties": false, + "properties": { + "ansible": { + "additionalProperties": false, + "properties": { + "cmdline": { + "description": "Extra parameters passed to the corresponding command", + "type": "string" + }, + "config": { + "additionalProperties": false, + "properties": { + "help": { + "default": false, + "description": "Help options for ansible-config command in stdout mode", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "path": { + "description": "Specify the path to the ansible configuration file", + "type": "string" + } + } + }, + "doc": { + "additionalProperties": false, + "properties": { + "help": { + "default": false, + "description": "Help options for ansible-doc command in stdout mode", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "plugin": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Specify the plugin name", + "type": "string" + }, + "type": { + "default": "module", + "description": "Specify the plugin type, 'become', 'cache', 'callback', 'cliconf', 'connection', 'httpapi', 'inventory', 'lookup', 'module', 'netconf', 'shell', 'strategy' or 'vars'", + "enum": [ + "become", + "cache", + "callback", + "cliconf", + "connection", + "httpapi", + "inventory", + "lookup", + "module", + "netconf", + "shell", + "strategy", + "vars" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "inventory": { + "additionalProperties": false, + "properties": { + "entries": { + "description": "Specify an inventory file path or comma separated host list", + "items": { + "type": "string" + }, + "type": "array" + }, + "help": { + "default": false, + "description": "Help options for ansible-inventory command in stdout mode", + "enum": [ + true, + false + ], + "type": "boolean" + } + } + }, + "playbook": { + "additionalProperties": false, + "properties": { + "help": { + "default": false, + "description": "Help options for ansible-playbook command in stdout mode", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "path": { + "description": "Specify the playbook name", + "type": "string" + } + } + } + }, + "type": "object" + }, + "ansible-builder": { + "additionalProperties": false, + "properties": { + "help": { + "default": false, + "description": "Help options for ansible-builder command in stdout mode", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "workdir": { + "default": ".", + "description": "Specify the path that contains ansible-builder manifest files", + "type": "string" + } + }, + "type": "object" + }, + "ansible-lint": { + "additionalProperties": false, + "properties": { + "config": { + "description": "Specify the path to the ansible-lint configuration file", + "type": "string" + }, + "lintables": { + "description": "Path to files on which to run ansible-lint", + "type": "string" + } + }, + "type": "object" + }, + "ansible-runner": { + "additionalProperties": false, + "properties": { + "artifact-dir": { + "description": "The directory path to store artifacts generated by ansible-runner", + "type": "string" + }, + "rotate-artifacts-count": { + "description": "Keep ansible-runner artifact directories, for last n runs, if set to 0 artifact directories won't be deleted", + "type": "integer" + }, + "timeout": { + "description": "The timeout value after which ansible-runner will forcefully stop the execution", + "type": "integer" + } + }, + "type": "object" + }, + "app": { + "default": "welcome", + "description": "Subcommands", + "enum": [ + "builder", + "collections", + "config", + "doc", + "exec", + "images", + "inventory", + "lint", + "replay", + "run", + "settings", + "welcome" + ], + "type": "string" + }, + "collection-doc-cache-path": { + "default": "~/.cache/ansible-navigator/collection_doc_cache.db", + "description": "The path to collection doc cache", + "type": "string" + }, + "color": { + "additionalProperties": false, + "properties": { + "enable": { + "default": true, + "description": "Enable the use of color for mode interactive and stdout", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "osc4": { + "default": true, + "description": "Enable or disable terminal color changing support with OSC 4", + "enum": [ + true, + false + ], + "type": "boolean" + } + }, + "type": "object" + }, + "editor": { + "additionalProperties": false, + "properties": { + "command": { + "default": "vi +{line_number} {filename}", + "description": "Specify the editor command", + "type": "string" + }, + "console": { + "default": true, + "description": "Specify if the editor is console based", + "enum": [ + true, + false + ], + "type": "boolean" + } + }, + "type": "object" + }, + "exec": { + "additionalProperties": false, + "properties": { + "command": { + "default": "/bin/bash", + "description": "Specify the command to run within the execution environment", + "type": "string" + }, + "shell": { + "default": true, + "description": "Specify the exec command should be run in a shell", + "enum": [ + true, + false + ], + "type": "boolean" + } + }, + "type": "object" + }, + "execution-environment": { + "additionalProperties": false, + "properties": { + "container-engine": { + "default": "auto", + "description": "Specify the container engine (auto=podman then docker)", + "enum": [ + "auto", + "podman", + "docker" + ], + "type": "string" + }, + "container-options": { + "description": "Extra parameters passed to the container engine command", + "items": { + "type": "string" + }, + "type": "array" + }, + "enabled": { + "default": true, + "description": "Enable or disable the use of an execution environment", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "environment-variables": { + "additionalProperties": false, + "properties": { + "pass": { + "description": "Specify an existing environment variable to be passed through to and set within the execution environment (--penv MY_VAR)", + "items": { + "type": "string" + }, + "type": "array" + }, + "set": { + "description": "Specify an environment variable and a value to be set within the execution environment (--senv MY_VAR=42)", + "type": "object" + } + }, + "type": "object" + }, + "image": { + "default": "quay.io/ansible/creator-ee:v0.4.2", + "description": "Specify the name of the execution environment image", + "type": "string" + }, + "pull": { + "additionalProperties": false, + "properties": { + "arguments": { + "description": "Specify any additional parameters that should be added to the pull command when pulling an execution environment from a container registry. e.g. --pa='--tls-verify=false'", + "items": { + "type": "string" + }, + "type": "array" + }, + "policy": { + "default": "tag", + "description": "Specify the image pull policy always:Always pull the image, missing:Pull if not locally available, never:Never pull the image, tag:if the image tag is 'latest', always pull the image, otherwise pull if not locally available", + "enum": [ + "always", + "missing", + "never", + "tag" + ], + "type": "string" + } + } + }, + "volume-mounts": { + "additionalProperties": false, + "description": "Specify volume to be bind mounted within an execution environment (--eev /home/user/test:/home/user/test:Z)", + "items": { + "additionalProperties": false, + "properties": { + "dest": { + "type": "string" + }, + "options": { + "type": "string" + }, + "src": { + "type": "string" + } + }, + "required": [ + "src", + "dest" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "images": { + "additionalProperties": false, + "properties": { + "details": { + "default": [ + "everything" + ], + "description": "Provide detailed information about the selected execution environment image", + "items": { + "enum": [ + "ansible_collections", + "ansible_version", + "everything", + "os_release", + "python_packages", + "python_version", + "redhat_release", + "system_packages" + ], + "type": "string" + }, + "type": "array" + } + } + }, + "inventory-columns": { + "description": "Specify a host attribute to show in the inventory view", + "items": { + "type": "string" + }, + "type": "array" + }, + "logging": { + "additionalProperties": false, + "properties": { + "append": { + "default": true, + "description": "Specify if log messages should be appended to an existing log file, otherwise a new log file will be created per session", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "file": { + "default": "./ansible-navigator.log", + "description": "Specify the full path for the ansible-navigator log file", + "type": "string" + }, + "level": { + "default": "warning", + "description": "Specify the ansible-navigator log level", + "enum": [ + "debug", + "info", + "warning", + "error", + "critical" + ], + "type": "string" + } + }, + "type": "object" + }, + "mode": { + "default": "interactive", + "description": "Specify the user-interface mode", + "enum": [ + "stdout", + "interactive" + ], + "type": "string" + }, + "playbook-artifact": { + "additionalProperties": false, + "properties": { + "enable": { + "default": true, + "description": "Enable or disable the creation of artifacts for completed playbooks. Note: not compatible with '--mode stdout' when playbooks require user input", + "enum": [ + true, + false + ], + "type": "boolean" + }, + "replay": { + "description": "Specify the path for the playbook artifact to replay", + "type": "string" + }, + "save-as": { + "default": "{playbook_dir}/{playbook_name}-artifact-{time_stamp}.json", + "description": "Specify the name for artifacts created from completed playbooks", + "type": "string" + } + }, + "type": "object" + }, + "settings": { + "additionalProperties": false, + "properties": { + "effective": { + "default": false, + "description": "Show the effective settings. Defaults, CLI parameters, environment variables, and the settings file will be combined", + "type": "boolean" + }, + "sample": { + "default": false, + "description": "Generate a sample settings file", + "type": "boolean" + }, + "schema": { + "default": "json", + "description": "Generate a schema for the settings file ('json'= draft-07 JSON Schema)", + "enum": [ + "json" + ], + "type": "string" + }, + "sources": { + "default": false, + "description": "Show the source of each current settings entry", + "type": "boolean" + } + } + }, + "time-zone": { + "default": "UTC", + "description": "Specify the IANA time zone to use or 'local' to use the system time zone", + "type": "string" + } + } + } + }, + "required": [ + "ansible-navigator" + ], + "title": "ansible-navigator settings v2.0", + "type": "object", + "version": "2.0" +} diff --git a/src/ansiblelint/schemas/execution-environment.json b/src/ansiblelint/schemas/execution-environment.json new file mode 100644 index 0000000000..82bb04f162 --- /dev/null +++ b/src/ansiblelint/schemas/execution-environment.json @@ -0,0 +1,70 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-ee.json", + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "description": "See https://docs.ansible.com/automation-controller/latest/html/userguide/ee_reference.html", + "examples": ["execution-environment.yml"], + "properties": { + "additional_build_steps": { + "properties": { + "append": { + "examples": ["RUN cat /etc/os-release"], + "type": ["string", "array"] + }, + "prepend": { + "examples": ["RUN cat /etc/os-release"], + "type": ["string", "array"] + } + }, + "title": "Commands to append or prepend to container build process.", + "type": "object" + }, + "ansible_config": { + "examples": ["ansible.cfg"], + "title": "Ansible configuration file", + "type": "string" + }, + "build_arg_defaults": { + "additionalProperties": true, + "properties": { + "EE_BASE_IMAGE": { + "type": "string" + } + }, + "type": "object" + }, + "dependencies": { + "description": "Allows adding system, python or galaxy dependencies.", + "properties": { + "galaxy": { + "examples": ["requirements.yml"], + "markdownDescription": "Example `requirements.yml`", + "title": "Optional galaxy file", + "type": "string" + }, + "python": { + "examples": ["requirements.txt"], + "markdownDescription": "Example `requirements.txt`", + "title": "Optional python package dependencies", + "type": "string" + }, + "system": { + "examples": ["bindep.txt"], + "markdownDescription": "Example `bindep.txt`", + "title": "Optional system dependencies using bindep format", + "type": "string" + } + }, + "title": "Dependencies", + "type": "object" + }, + "version": { + "enum": [1], + "title": "Version", + "type": "integer" + } + }, + "required": ["version", "dependencies"], + "title": "Ansible Execution Environment Schema", + "type": "object" +} diff --git a/src/ansiblelint/schemas/galaxy.json b/src/ansiblelint/schemas/galaxy.json new file mode 100644 index 0000000000..35005bb7fc --- /dev/null +++ b/src/ansiblelint/schemas/galaxy.json @@ -0,0 +1,547 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-galaxy.json", + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "definitions": { + "CollectionVersionConstraintModel": { + "additionalProperties": false, + "title": "CollectionVersionConstraintModel", + "type": "string" + }, + "SPDXLicense": { + "$ref": "#/definitions/SPDXLicenseEnum", + "title": "SPDXLicense" + }, + "SPDXLicenseEnum": { + "description": "An enumeration.", + "enum": [ + "0BSD", + "AAL", + "ADSL", + "AFL-1.1", + "AFL-1.2", + "AFL-2.0", + "AFL-2.1", + "AFL-3.0", + "AGPL-1.0-only", + "AGPL-1.0-or-later", + "AGPL-3.0-only", + "AGPL-3.0-or-later", + "AMDPLPA", + "AML", + "AMPAS", + "ANTLR-PD", + "ANTLR-PD-fallback", + "APAFML", + "APL-1.0", + "APSL-1.0", + "APSL-1.1", + "APSL-1.2", + "APSL-2.0", + "Abstyles", + "Adobe-2006", + "Adobe-Glyph", + "Afmparse", + "Aladdin", + "Apache-1.0", + "Apache-1.1", + "Apache-2.0", + "Artistic-1.0", + "Artistic-1.0-Perl", + "Artistic-1.0-cl8", + "Artistic-2.0", + "BSD-1-Clause", + "BSD-2-Clause", + "BSD-2-Clause-Patent", + "BSD-2-Clause-Views", + "BSD-3-Clause", + "BSD-3-Clause-Attribution", + "BSD-3-Clause-Clear", + "BSD-3-Clause-LBNL", + "BSD-3-Clause-Modification", + "BSD-3-Clause-No-Military-License", + "BSD-3-Clause-No-Nuclear-License", + "BSD-3-Clause-No-Nuclear-License-2014", + "BSD-3-Clause-No-Nuclear-Warranty", + "BSD-3-Clause-Open-MPI", + "BSD-4-Clause", + "BSD-4-Clause-Shortened", + "BSD-4-Clause-UC", + "BSD-Protection", + "BSD-Source-Code", + "BSL-1.0", + "BUSL-1.1", + "Bahyph", + "Barr", + "Beerware", + "BitTorrent-1.0", + "BitTorrent-1.1", + "BlueOak-1.0.0", + "Borceux", + "C-UDA-1.0", + "CAL-1.0", + "CAL-1.0-Combined-Work-Exception", + "CATOSL-1.1", + "CC-BY-1.0", + "CC-BY-2.0", + "CC-BY-2.5", + "CC-BY-3.0", + "CC-BY-3.0-AT", + "CC-BY-3.0-US", + "CC-BY-4.0", + "CC-BY-NC-1.0", + "CC-BY-NC-2.0", + "CC-BY-NC-2.5", + "CC-BY-NC-3.0", + "CC-BY-NC-4.0", + "CC-BY-NC-ND-1.0", + "CC-BY-NC-ND-2.0", + "CC-BY-NC-ND-2.5", + "CC-BY-NC-ND-3.0", + "CC-BY-NC-ND-3.0-IGO", + "CC-BY-NC-ND-4.0", + "CC-BY-NC-SA-1.0", + "CC-BY-NC-SA-2.0", + "CC-BY-NC-SA-2.5", + "CC-BY-NC-SA-3.0", + "CC-BY-NC-SA-4.0", + "CC-BY-ND-1.0", + "CC-BY-ND-2.0", + "CC-BY-ND-2.5", + "CC-BY-ND-3.0", + "CC-BY-ND-4.0", + "CC-BY-SA-1.0", + "CC-BY-SA-2.0", + "CC-BY-SA-2.0-UK", + "CC-BY-SA-2.1-JP", + "CC-BY-SA-2.5", + "CC-BY-SA-3.0", + "CC-BY-SA-3.0-AT", + "CC-BY-SA-4.0", + "CC-PDDC", + "CC0-1.0", + "CDDL-1.0", + "CDDL-1.1", + "CDL-1.0", + "CDLA-Permissive-1.0", + "CDLA-Sharing-1.0", + "CECILL-1.0", + "CECILL-1.1", + "CECILL-2.0", + "CECILL-2.1", + "CECILL-B", + "CECILL-C", + "CERN-OHL-1.1", + "CERN-OHL-1.2", + "CERN-OHL-P-2.0", + "CERN-OHL-S-2.0", + "CERN-OHL-W-2.0", + "CNRI-Jython", + "CNRI-Python", + "CNRI-Python-GPL-Compatible", + "CPAL-1.0", + "CPL-1.0", + "CPOL-1.02", + "CUA-OPL-1.0", + "Caldera", + "ClArtistic", + "Condor-1.1", + "Crossword", + "CrystalStacker", + "Cube", + "D-FSL-1.0", + "DOC", + "DRL-1.0", + "DSDP", + "Dotseqn", + "ECL-1.0", + "ECL-2.0", + "EFL-1.0", + "EFL-2.0", + "EPICS", + "EPL-1.0", + "EPL-2.0", + "EUDatagrid", + "EUPL-1.0", + "EUPL-1.1", + "EUPL-1.2", + "Entessa", + "ErlPL-1.1", + "Eurosym", + "FSFAP", + "FSFUL", + "FSFULLR", + "FTL", + "Fair", + "Frameworx-1.0", + "FreeBSD-DOC", + "FreeImage", + "GD", + "GFDL-1.1-invariants-only", + "GFDL-1.1-invariants-or-later", + "GFDL-1.1-no-invariants-only", + "GFDL-1.1-no-invariants-or-later", + "GFDL-1.1-only", + "GFDL-1.1-or-later", + "GFDL-1.2-invariants-only", + "GFDL-1.2-invariants-or-later", + "GFDL-1.2-no-invariants-only", + "GFDL-1.2-no-invariants-or-later", + "GFDL-1.2-only", + "GFDL-1.2-or-later", + "GFDL-1.3-invariants-only", + "GFDL-1.3-invariants-or-later", + "GFDL-1.3-no-invariants-only", + "GFDL-1.3-no-invariants-or-later", + "GFDL-1.3-only", + "GFDL-1.3-or-later", + "GL2PS", + "GLWTPL", + "GPL-1.0-only", + "GPL-1.0-or-later", + "GPL-2.0-only", + "GPL-2.0-or-later", + "GPL-3.0-only", + "GPL-3.0-or-later", + "Giftware", + "Glide", + "Glulxe", + "HPND", + "HPND-sell-variant", + "HTMLTIDY", + "HaskellReport", + "Hippocratic-2.1", + "IBM-pibs", + "ICU", + "IJG", + "IPA", + "IPL-1.0", + "ISC", + "ImageMagick", + "Imlib2", + "Info-ZIP", + "Intel", + "Intel-ACPI", + "Interbase-1.0", + "JPNIC", + "JSON", + "JasPer-2.0", + "LAL-1.2", + "LAL-1.3", + "LGPL-2.0-only", + "LGPL-2.0-or-later", + "LGPL-2.1-only", + "LGPL-2.1-or-later", + "LGPL-3.0-only", + "LGPL-3.0-or-later", + "LGPLLR", + "LPL-1.0", + "LPL-1.02", + "LPPL-1.0", + "LPPL-1.1", + "LPPL-1.2", + "LPPL-1.3a", + "LPPL-1.3c", + "Latex2e", + "Leptonica", + "LiLiQ-P-1.1", + "LiLiQ-R-1.1", + "LiLiQ-Rplus-1.1", + "Libpng", + "Linux-OpenIB", + "MIT", + "MIT-0", + "MIT-CMU", + "MIT-Modern-Variant", + "MIT-advertising", + "MIT-enna", + "MIT-feh", + "MIT-open-group", + "MITNFA", + "MPL-1.0", + "MPL-1.1", + "MPL-2.0", + "MPL-2.0-no-copyleft-exception", + "MS-PL", + "MS-RL", + "MTLL", + "MakeIndex", + "MirOS", + "Motosoto", + "MulanPSL-1.0", + "MulanPSL-2.0", + "Multics", + "Mup", + "NAIST-2003", + "NASA-1.3", + "NBPL-1.0", + "NCGL-UK-2.0", + "NCSA", + "NGPL", + "NIST-PD", + "NIST-PD-fallback", + "NLOD-1.0", + "NLPL", + "NOSL", + "NPL-1.0", + "NPL-1.1", + "NPOSL-3.0", + "NRL", + "NTP", + "NTP-0", + "Naumen", + "Net-SNMP", + "NetCDF", + "Newsletr", + "Nokia", + "Noweb", + "O-UDA-1.0", + "OCCT-PL", + "OCLC-2.0", + "ODC-By-1.0", + "ODbL-1.0", + "OFL-1.0", + "OFL-1.0-RFN", + "OFL-1.0-no-RFN", + "OFL-1.1", + "OFL-1.1-RFN", + "OFL-1.1-no-RFN", + "OGC-1.0", + "OGDL-Taiwan-1.0", + "OGL-Canada-2.0", + "OGL-UK-1.0", + "OGL-UK-2.0", + "OGL-UK-3.0", + "OGTSL", + "OLDAP-1.1", + "OLDAP-1.2", + "OLDAP-1.3", + "OLDAP-1.4", + "OLDAP-2.0", + "OLDAP-2.0.1", + "OLDAP-2.1", + "OLDAP-2.2", + "OLDAP-2.2.1", + "OLDAP-2.2.2", + "OLDAP-2.3", + "OLDAP-2.4", + "OLDAP-2.5", + "OLDAP-2.6", + "OLDAP-2.7", + "OLDAP-2.8", + "OML", + "OPL-1.0", + "OSET-PL-2.1", + "OSL-1.0", + "OSL-1.1", + "OSL-2.0", + "OSL-2.1", + "OSL-3.0", + "OpenSSL", + "PDDL-1.0", + "PHP-3.0", + "PHP-3.01", + "PSF-2.0", + "Parity-6.0.0", + "Parity-7.0.0", + "Plexus", + "PolyForm-Noncommercial-1.0.0", + "PolyForm-Small-Business-1.0.0", + "PostgreSQL", + "Python-2.0", + "QPL-1.0", + "Qhull", + "RHeCos-1.1", + "RPL-1.1", + "RPL-1.5", + "RPSL-1.0", + "RSA-MD", + "RSCPL", + "Rdisc", + "Ruby", + "SAX-PD", + "SCEA", + "SGI-B-1.0", + "SGI-B-1.1", + "SGI-B-2.0", + "SHL-0.5", + "SHL-0.51", + "SISSL", + "SISSL-1.2", + "SMLNJ", + "SMPPL", + "SNIA", + "SPL-1.0", + "SSH-OpenSSH", + "SSH-short", + "SSPL-1.0", + "SWL", + "Saxpath", + "Sendmail", + "Sendmail-8.23", + "SimPL-2.0", + "Sleepycat", + "Spencer-86", + "Spencer-94", + "Spencer-99", + "SugarCRM-1.1.3", + "TAPR-OHL-1.0", + "TCL", + "TCP-wrappers", + "TMate", + "TORQUE-1.1", + "TOSL", + "TU-Berlin-1.0", + "TU-Berlin-2.0", + "UCL-1.0", + "UPL-1.0", + "Unicode-DFS-2015", + "Unicode-DFS-2016", + "Unicode-TOU", + "Unlicense", + "VOSTROM", + "VSL-1.0", + "Vim", + "W3C", + "W3C-19980720", + "W3C-20150513", + "WTFPL", + "Watcom-1.0", + "Wsuipa", + "X11", + "XFree86-1.1", + "XSkat", + "Xerox", + "Xnet", + "YPL-1.0", + "YPL-1.1", + "ZPL-1.1", + "ZPL-2.0", + "ZPL-2.1", + "Zed", + "Zend-2.0", + "Zimbra-1.3", + "Zimbra-1.4", + "Zlib", + "blessing", + "bzip2-1.0.5", + "bzip2-1.0.6", + "copyleft-next-0.3.0", + "copyleft-next-0.3.1", + "curl", + "diffmark", + "dvipdfm", + "eGenix", + "etalab-2.0", + "gSOAP-1.3b", + "gnuplot", + "iMatix", + "libpng-2.0", + "libselinux-1.0", + "libtiff", + "mpich2", + "psfrag", + "psutils", + "xinetd", + "xpp", + "zlib-acknowledgement" + ], + "title": "SPDXLicenseEnum" + } + }, + "examples": ["galaxy.yml"], + "properties": { + "authors": { + "items": { + "type": "string" + }, + "title": "Authors", + "type": "array" + }, + "build_ignore": { + "items": { + "type": "string" + }, + "title": "Build Ignore", + "type": "array" + }, + "dependencies": { + "additionalProperties": { + "$ref": "#/definitions/CollectionVersionConstraintModel" + }, + "title": "Dependencies", + "type": "object" + }, + "description": { + "title": "Description", + "type": "string" + }, + "documentation": { + "title": "Documentation", + "type": "string" + }, + "homepage": { + "title": "Homepage", + "type": "string" + }, + "issues": { + "title": "Issues", + "type": "string" + }, + "license": { + "items": { + "$ref": "#/definitions/SPDXLicense" + }, + "title": "License", + "type": "array" + }, + "license_file": { + "title": "License File", + "type": "string" + }, + "name": { + "minLength": 2, + "pattern": "[a-z][a-z0-9_]+", + "title": "Name", + "type": "string" + }, + "namespace": { + "minLength": 2, + "pattern": "[a-z][a-z0-9_]+", + "title": "Namespace", + "type": "string" + }, + "readme": { + "markdownDescription": "The path to the Markdown (.md) readme file. This path is relative to the root of the collection.\nSee [metadata structure](https://docs.ansible.com/ansible/latest/dev_guide/collections_galaxy_meta.html)", + "title": "Readme", + "type": "string" + }, + "repository": { + "title": "Repository", + "type": "string" + }, + "tags": { + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "version": { + "minLength": 5, + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Version", + "type": "string" + } + }, + "required": [ + "namespace", + "name", + "version", + "readme", + "authors", + "description" + ], + "title": "Ansible galaxy.yml Schema", + "type": "object" +} diff --git a/src/ansiblelint/schemas/inventory.json b/src/ansiblelint/schemas/inventory.json new file mode 100644 index 0000000000..ec1ecdbe62 --- /dev/null +++ b/src/ansiblelint/schemas/inventory.json @@ -0,0 +1,38 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-inventory.json", + "$schema": "http://json-schema.org/draft-07/schema", + "definitions": { + "group": { + "properties": { + "children": { + "patternProperties": { + "[a-zA-Z-_0-9]": { + "$ref": "#/definitions/group" + } + } + }, + "hosts": { + "patternProperties": { + "[a-zA-Z.-_0-9]": { + "type": ["object", "null"] + } + }, + "type": ["object", "string"] + }, + "vars": { + "type": "object" + } + }, + "type": "object" + } + }, + "description": "Ansible Inventory Schema", + "examples": ["inventory.yaml", "inventory.yml"], + "properties": { + "all": { + "$ref": "#/definitions/group" + } + }, + "title": "Ansible Inventory Schema", + "type": "object" +} diff --git a/src/ansiblelint/schemas/meta-runtime.json b/src/ansiblelint/schemas/meta-runtime.json new file mode 100644 index 0000000000..cfaa685a9e --- /dev/null +++ b/src/ansiblelint/schemas/meta-runtime.json @@ -0,0 +1,81 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-meta-runtime.json", + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "definitions": { + "ActionGroup": { + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/Metadata" + } + ] + }, + "type": "array" + }, + "Metadata": { + "properties": { + "metadata": { + "properties": { + "extend_group": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "Redirect": { + "properties": { + "redirect": { + "type": "string" + } + }, + "type": "object" + } + }, + "description": "See https://docs.ansible.com/ansible/devel/dev_guide/developing_collections_structure.html#meta-directory", + "examples": ["**/meta/runtime.yml"], + "properties": { + "action_groups": { + "additionalProperties": { + "$ref": "#/definitions/ActionGroup" + }, + "description": "A mapping of groups and the list of action plugin and module names they contain. They may also have a special ‘metadata’ dictionary in the list, which can be used to include actions from other groups.", + "title": "Action Groups", + "type": "object" + }, + "import_redirection": { + "additionalProperties": { + "$ref": "#/definitions/Redirect" + }, + "description": "A mapping of names for Python import statements and their redirected locations.", + "title": "Import Redirection", + "type": "object" + }, + "plugin_routing": { + "markdownDescription": "Content in a collection that Ansible needs to load from another location or that has been deprecated/removed. The top level keys of plugin_routing are types of plugins, with individual plugin names as subkeys. To define a new location for a plugin, set the redirect field to another name. To deprecate a plugin, use the deprecation field to provide a custom warning message and the removal version or date. If the plugin has been renamed or moved to a new location, the redirect field should also be provided. If a plugin is being removed entirely, tombstone can be used for the fatal error message and removal version or date.", + "properties": { + "inventory": {}, + "module_utils": {}, + "modules": {} + }, + "title": "Plugin Routing", + "type": "object" + }, + "requires_ansible": { + "examples": [">=2.10,<2.11"], + "title": "The version of Ansible Core (ansible-core) required to use the collection. Multiple versions can be separated with a comma.", + "type": "string" + } + }, + "title": "Ansible Meta Runtime Schema", + "type": "object" +} diff --git a/src/ansiblelint/schemas/meta.json b/src/ansiblelint/schemas/meta.json new file mode 100644 index 0000000000..8644b04482 --- /dev/null +++ b/src/ansiblelint/schemas/meta.json @@ -0,0 +1,1220 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-meta.json", + "$schema": "http://json-schema.org/draft-07/schema", + "anyOf": [ + { + "type": "null" + }, + { + "additionalProperties": false, + "properties": { + "allow_duplicates": { + "title": "Allow Duplicates", + "type": "boolean" + }, + "collections": { + "items": { + "markdownDescription": "See [Using collections in roles](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#using-collections-in-roles) and [collection naming conventions](https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_in_groups.html#naming-conventions)", + "pattern": "^[a-z_]+\\.[a-z_]+$", + "type": "string" + }, + "type": "array" + }, + "dependencies": { + "items": { + "$ref": "#/definitions/DependencyModel" + }, + "title": "Dependencies", + "type": "array" + }, + "galaxy_info": { + "$ref": "#/definitions/GalaxyInfoModel" + } + }, + "required": ["galaxy_info"], + "type": "object" + } + ], + "definitions": { + "AIXPlatformModel": { + "properties": { + "name": { + "const": "AIX", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["6.1", "7.1", "7.2", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "AIXPlatformModel", + "type": "object" + }, + "AlpinePlatformModel": { + "properties": { + "name": { + "const": "Alpine", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "AlpinePlatformModel", + "type": "object" + }, + "AmazonPlatformModel": { + "properties": { + "name": { + "const": "Amazon", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "2013.03", + "2013.09", + "2014.03", + "2014.09", + "2015.03", + "2015.09", + "2016.03", + "2016.09", + "2017.03", + "2017.09", + "2017.12", + "2018.03", + "Candidate", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "AmazonPlatformModel", + "type": "object" + }, + "Amazon_Linux_2PlatformModel": { + "properties": { + "name": { + "const": "Amazon Linux 2", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "Amazon Linux 2PlatformModel", + "type": "object" + }, + "ArchLinuxPlatformModel": { + "properties": { + "name": { + "const": "ArchLinux", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "ArchLinuxPlatformModel", + "type": "object" + }, + "ClearLinuxPlatformModel": { + "properties": { + "name": { + "const": "ClearLinux", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "ClearLinuxPlatformModel", + "type": "object" + }, + "CumulusPlatformModel": { + "properties": { + "name": { + "const": "Cumulus", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["2.5", "3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "CumulusPlatformModel", + "type": "object" + }, + "DebianPlatformModel": { + "properties": { + "name": { + "const": "Debian", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "bookworm", + "bullseye", + "buster", + "etch", + "jessie", + "lenny", + "sid", + "squeeze", + "stretch", + "wheezy", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "DebianPlatformModel", + "type": "object" + }, + "DellOSPlatformModel": { + "properties": { + "name": { + "const": "DellOS", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["10", "6", "9", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "DellOSPlatformModel", + "type": "object" + }, + "DependencyModel": { + "additionalProperties": false, + "anyOf": [ + { + "required": ["role"] + }, + { + "required": ["src"] + }, + { + "required": ["name"] + } + ], + "markdownDescription": "See https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#role-dependencies and https://github.com/ansible/ansible/blob/devel/lib/ansible/playbook/role/metadata.py#L79", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "role": { + "title": "Role", + "type": "string" + }, + "scm": { + "enum": ["hg", "git"], + "title": "Scm", + "type": "string" + }, + "src": { + "title": "Src", + "type": "string" + }, + "tags": { + "items": { + "type": "string" + }, + "title": "Tags", + "type": "array" + }, + "vars": { + "title": "Vars", + "type": "object" + }, + "version": { + "title": "Version", + "type": "string" + } + }, + "title": "Dependency entry", + "type": "object" + }, + "DevuanPlatformModel": { + "properties": { + "name": { + "const": "Devuan", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["ascii", "beowulf", "ceres", "jessie", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "DevuanPlatformModel", + "type": "object" + }, + "DragonFlyBSDPlatformModel": { + "properties": { + "name": { + "const": "DragonFlyBSD", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["5.2", "5.4", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "DragonFlyBSDPlatformModel", + "type": "object" + }, + "ELPlatformModel": { + "properties": { + "name": { + "const": "EL", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["5", "6", "7", "8", "9", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "ELPlatformModel", + "type": "object" + }, + "FedoraPlatformModel": { + "properties": { + "name": { + "const": "Fedora", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "FedoraPlatformModel", + "type": "object" + }, + "FreeBSDPlatformModel": { + "properties": { + "name": { + "const": "FreeBSD", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "10.0", + "10.1", + "10.2", + "10.3", + "10.4", + "11.0", + "11.1", + "11.2", + "11.3", + "11.4", + "12.0", + "12.1", + "12.2", + "13.0", + "8.0", + "8.1", + "8.2", + "8.3", + "8.4", + "9.0", + "9.1", + "9.2", + "9.3", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "FreeBSDPlatformModel", + "type": "object" + }, + "GalaxyInfoModel": { + "additionalProperties": false, + "properties": { + "author": { + "minLength": 2, + "pattern": "[a-z0-9][a-z0-9_]+", + "title": "Author", + "type": "string" + }, + "company": { + "title": "Company", + "type": "string" + }, + "description": { + "title": "Description", + "type": "string" + }, + "galaxy_tags": { + "items": { + "type": "string" + }, + "markdownDescription": "See https://galaxy.ansible.com/docs/contributing/creating_role.html", + "title": "Galaxy Tags", + "type": "array" + }, + "issue_tracker_url": { + "title": "Issue Tracker Url", + "type": "string" + }, + "license": { + "title": "License", + "type": "string" + }, + "min_ansible_container_version": { + "title": "Min Ansible Container Version", + "type": "string" + }, + "min_ansible_version": { + "title": "Min Ansible Version", + "type": "string" + }, + "platforms": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/AIXPlatformModel" + }, + { + "$ref": "#/definitions/AlpinePlatformModel" + }, + { + "$ref": "#/definitions/AmazonPlatformModel" + }, + { + "$ref": "#/definitions/Amazon_Linux_2PlatformModel" + }, + { + "$ref": "#/definitions/aosPlatformModel" + }, + { + "$ref": "#/definitions/ArchLinuxPlatformModel" + }, + { + "$ref": "#/definitions/ClearLinuxPlatformModel" + }, + { + "$ref": "#/definitions/CumulusPlatformModel" + }, + { + "$ref": "#/definitions/DebianPlatformModel" + }, + { + "$ref": "#/definitions/DellOSPlatformModel" + }, + { + "$ref": "#/definitions/DevuanPlatformModel" + }, + { + "$ref": "#/definitions/DragonFlyBSDPlatformModel" + }, + { + "$ref": "#/definitions/ELPlatformModel" + }, + { + "$ref": "#/definitions/eosPlatformModel" + }, + { + "$ref": "#/definitions/FedoraPlatformModel" + }, + { + "$ref": "#/definitions/FreeBSDPlatformModel" + }, + { + "$ref": "#/definitions/GenericBSDPlatformModel" + }, + { + "$ref": "#/definitions/GenericLinuxPlatformModel" + }, + { + "$ref": "#/definitions/GenericUNIXPlatformModel" + }, + { + "$ref": "#/definitions/GentooPlatformModel" + }, + { + "$ref": "#/definitions/HardenedBSDPlatformModel" + }, + { + "$ref": "#/definitions/IOSPlatformModel" + }, + { + "$ref": "#/definitions/JunosPlatformModel" + }, + { + "$ref": "#/definitions/macOSPlatformModel" + }, + { + "$ref": "#/definitions/MacOSXPlatformModel" + }, + { + "$ref": "#/definitions/NXOSPlatformModel" + }, + { + "$ref": "#/definitions/OpenBSDPlatformModel" + }, + { + "$ref": "#/definitions/opensusePlatformModel" + }, + { + "$ref": "#/definitions/os10PlatformModel" + }, + { + "$ref": "#/definitions/PAN-OSPlatformModel" + }, + { + "$ref": "#/definitions/SLESPlatformModel" + }, + { + "$ref": "#/definitions/SmartOSPlatformModel" + }, + { + "$ref": "#/definitions/SolarisPlatformModel" + }, + { + "$ref": "#/definitions/SynologyPlatformModel" + }, + { + "$ref": "#/definitions/TMOSPlatformModel" + }, + { + "$ref": "#/definitions/UbuntuPlatformModel" + }, + { + "$ref": "#/definitions/vCenterPlatformModel" + }, + { + "$ref": "#/definitions/Void_LinuxPlatformModel" + }, + { + "$ref": "#/definitions/vSpherePlatformModel" + }, + { + "$ref": "#/definitions/WindowsPlatformModel" + } + ] + }, + "title": "Platforms", + "type": "array" + }, + "role_name": { + "minLength": 2, + "pattern": "[a-z][a-z0-9_]+", + "title": "Role Name", + "type": "string" + } + }, + "required": [ + "description", + "license", + "min_ansible_version", + "platforms" + ], + "title": "GalaxyInfoModel", + "type": "object" + }, + "GenericBSDPlatformModel": { + "properties": { + "name": { + "const": "GenericBSD", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "GenericBSDPlatformModel", + "type": "object" + }, + "GenericLinuxPlatformModel": { + "properties": { + "name": { + "const": "GenericLinux", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "GenericLinuxPlatformModel", + "type": "object" + }, + "GenericUNIXPlatformModel": { + "properties": { + "name": { + "const": "GenericUNIX", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "GenericUNIXPlatformModel", + "type": "object" + }, + "GentooPlatformModel": { + "properties": { + "name": { + "const": "Gentoo", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "GentooPlatformModel", + "type": "object" + }, + "HardenedBSDPlatformModel": { + "properties": { + "name": { + "const": "HardenedBSD", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["10", "11", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "HardenedBSDPlatformModel", + "type": "object" + }, + "IOSPlatformModel": { + "properties": { + "name": { + "const": "IOS", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "IOSPlatformModel", + "type": "object" + }, + "JunosPlatformModel": { + "properties": { + "name": { + "const": "Junos", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "JunosPlatformModel", + "type": "object" + }, + "MacOSXPlatformModel": { + "properties": { + "name": { + "const": "MacOSX", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "10.10", + "10.11", + "10.12", + "10.13", + "10.14", + "10.15", + "10.7", + "10.8", + "10.9", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "MacOSXPlatformModel", + "type": "object" + }, + "NXOSPlatformModel": { + "properties": { + "name": { + "const": "NXOS", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "NXOSPlatformModel", + "type": "object" + }, + "OpenBSDPlatformModel": { + "properties": { + "name": { + "const": "OpenBSD", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "5.6", + "5.7", + "5.8", + "5.9", + "6.0", + "6.1", + "6.2", + "6.3", + "6.4", + "6.5", + "6.6", + "6.7", + "6.8", + "6.9", + "7.0", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "OpenBSDPlatformModel", + "type": "object" + }, + "PAN-OSPlatformModel": { + "properties": { + "name": { + "const": "PAN-OS", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["7.1", "8.0", "8.1", "9.0", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "PAN-OSPlatformModel", + "type": "object" + }, + "SLESPlatformModel": { + "properties": { + "name": { + "const": "SLES", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "10SP3", + "10SP4", + "11", + "11SP1", + "11SP2", + "11SP3", + "11SP4", + "12", + "12SP1", + "12SP2", + "12SP3", + "12SP4", + "12SP5", + "15", + "15SP1", + "15SP2", + "15SP3", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "SLESPlatformModel", + "type": "object" + }, + "SmartOSPlatformModel": { + "properties": { + "name": { + "const": "SmartOS", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "SmartOSPlatformModel", + "type": "object" + }, + "SolarisPlatformModel": { + "properties": { + "name": { + "const": "Solaris", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["10", "11.0", "11.1", "11.2", "11.3", "11.4", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "SolarisPlatformModel", + "type": "object" + }, + "SynologyPlatformModel": { + "properties": { + "name": { + "const": "Synology", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["6.0", "6.1", "6.2", "7.0", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "SynologyPlatformModel", + "type": "object" + }, + "TMOSPlatformModel": { + "properties": { + "name": { + "const": "TMOS", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["12.1", "13.0", "13.1", "14.0", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "TMOSPlatformModel", + "type": "object" + }, + "UbuntuPlatformModel": { + "properties": { + "name": { + "const": "Ubuntu", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "artful", + "bionic", + "cosmic", + "cuttlefish", + "disco", + "eoan", + "focal", + "groovy", + "hirsute", + "impish", + "jammy", + "lucid", + "maverick", + "natty", + "oneiric", + "precise", + "quantal", + "raring", + "saucy", + "trusty", + "utopic", + "vivid", + "wily", + "xenial", + "yakkety", + "zesty", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "UbuntuPlatformModel", + "type": "object" + }, + "Void_LinuxPlatformModel": { + "properties": { + "name": { + "const": "Void Linux", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "Void LinuxPlatformModel", + "type": "object" + }, + "WindowsPlatformModel": { + "properties": { + "name": { + "const": "Windows", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "2008R2", + "2008x64", + "2008x86", + "2012", + "2012R2", + "2016", + "2019", + "2022", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "WindowsPlatformModel", + "type": "object" + }, + "aosPlatformModel": { + "properties": { + "name": { + "const": "aos", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "aosPlatformModel", + "type": "object" + }, + "eosPlatformModel": { + "properties": { + "name": { + "const": "eos", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "eosPlatformModel", + "type": "object" + }, + "macOSPlatformModel": { + "properties": { + "name": { + "const": "macOS", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "Big-Sur", + "Catalina", + "High-Sierra", + "Mojave", + "Monterey", + "Sierra", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "macOSPlatformModel", + "type": "object" + }, + "opensusePlatformModel": { + "properties": { + "name": { + "const": "opensuse", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": [ + "12.1", + "12.2", + "12.3", + "13.1", + "13.2", + "15.0", + "15.1", + "15.2", + "15.3", + "42.1", + "42.2", + "42.3", + "all" + ], + "type": "string" + }, + "type": "array" + } + }, + "title": "opensusePlatformModel", + "type": "object" + }, + "os10PlatformModel": { + "properties": { + "name": { + "const": "os10", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "os10PlatformModel", + "type": "object" + }, + "vCenterPlatformModel": { + "properties": { + "name": { + "const": "vCenter", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["5.5", "6.0", "6.5", "6.7", "7.0", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "vCenterPlatformModel", + "type": "object" + }, + "vSpherePlatformModel": { + "properties": { + "name": { + "const": "vSphere", + "title": "Name", + "type": "string" + }, + "versions": { + "default": "all", + "items": { + "enum": ["5.5", "6.0", "6.5", "6.7", "7.0", "all"], + "type": "string" + }, + "type": "array" + } + }, + "title": "vSpherePlatformModel", + "type": "object" + } + }, + "examples": ["meta/main.yml"], + "title": "Ansible Meta Schema" +} diff --git a/src/ansiblelint/schemas/requirements.json b/src/ansiblelint/schemas/requirements.json new file mode 100644 index 0000000000..d49fa0ada7 --- /dev/null +++ b/src/ansiblelint/schemas/requirements.json @@ -0,0 +1,135 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-requirements.json", + "$schema": "http://json-schema.org/draft-07/schema", + "anyOf": [ + { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/RoleModel" + }, + { + "$ref": "#/definitions/IncludeModel" + } + ] + }, + "type": "array" + }, + { + "$ref": "#/definitions/RequirementsV2Model" + } + ], + "definitions": { + "CollectionModel": { + "additionalProperties": false, + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + }, + "type": { + "enum": ["galaxy", "url", "file", "git", "dir", "subdirs"], + "title": "Type", + "type": "string" + }, + "version": { + "title": "Version", + "type": "string" + } + }, + "title": "CollectionModel", + "type": "object" + }, + "CollectionStringModel": { + "title": "CollectionStringModel", + "type": "string" + }, + "IncludeModel": { + "properties": { + "include": { + "title": "Include", + "type": "string" + } + }, + "required": ["include"], + "title": "IncludeModel", + "type": "object" + }, + "RequirementsV2Model": { + "additionalProperties": false, + "anyOf": [ + { + "required": ["collections"] + }, + { + "required": ["roles"] + } + ], + "properties": { + "collections": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionModel" + }, + { + "$ref": "#/definitions/CollectionStringModel" + } + ] + }, + "title": "Collections", + "type": "array" + }, + "roles": { + "items": { + "$ref": "#/definitions/RoleModel" + }, + "title": "Roles", + "type": "array" + } + }, + "title": "Requirements v2", + "type": "object" + }, + "RoleModel": { + "additionalProperties": false, + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "scm": { + "anyOf": [ + { + "enum": ["git"], + "type": "string" + }, + { + "enum": ["hg"], + "type": "string" + } + ], + "default": "git", + "title": "Scm" + }, + "src": { + "title": "Src", + "type": "string" + }, + "version": { + "default": "master", + "title": "Version", + "type": "string" + } + }, + "title": "Role", + "type": "object" + } + }, + "examples": ["requirements.yml"], + "title": "Ansible Requirements Schema" +} diff --git a/src/ansiblelint/schemas/vars.json b/src/ansiblelint/schemas/vars.json new file mode 100644 index 0000000000..9898966705 --- /dev/null +++ b/src/ansiblelint/schemas/vars.json @@ -0,0 +1,29 @@ +{ + "$id": "https://raw.githubusercontent.com/ansible/schemas/main/f/ansible-vars.json", + "$schema": "http://json-schema.org/draft-07/schema", + "anyOf": [ + { + "additionalProperties": false, + "patternProperties": { + "^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[a-zA-Z_][\\w]*$": {} + }, + "type": "object" + }, + { + "pattern": "^\\$ANSIBLE_VAULT;", + "type": "string" + }, + { + "type": "null" + } + ], + "examples": [ + "playbooks/vars/*.yml", + "vars/*.yml", + "defaults/*.yml", + "host_vars/*.yml", + "group_vars/*.yml" + ], + "markdownDescription": "See [Using Variables](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html)", + "title": "Ansible Vars Schema" +} diff --git a/test/eco/bootstrap.result b/test/eco/bootstrap.result index 986daad5cc..6bffb30136 100644 --- a/test/eco/bootstrap.result +++ b/test/eco/bootstrap.result @@ -4,6 +4,15 @@ RC: 0 STDERR: WARNING Loading custom .yamllint config file, this extends our internal yamllint config. +WARNING Listing 3 violation(s) that are fatal +You can skip specific rules or tags by adding them to your configuration file: +# .config/ansible-lint.yml +warn_list: # or 'skip_list' to silence them completely + - experimental # all rules tagged as experimental + STDOUT: +.github/workflows/galaxy.yml:1: schema: Additional properties are not allowed ('jobs', 'true' were unexpected) (schema[galaxy]) +meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) +requirements.yml:1: schema: None is not of type 'array' (schema[requirements]) diff --git a/test/eco/colsystem.result b/test/eco/colsystem.result index 93e5b3e4f5..10684694bd 100644 --- a/test/eco/colsystem.result +++ b/test/eco/colsystem.result @@ -3,10 +3,11 @@ CMD: ansible-lint -f pep8 -x fqcn-builtins RC: 2 STDERR: -WARNING Listing 20 violation(s) that are fatal +WARNING Listing 44 violation(s) that are fatal You can skip specific rules or tags by adding them to your configuration file: # .config/ansible-lint.yml warn_list: # or 'skip_list' to silence them completely + - experimental # all rules tagged as experimental - load-failure # Failed to load or parse file. - no-handler # Tasks that run when changed should likely be handlers. - unnamed-task # All tasks should be named. @@ -17,21 +18,45 @@ warn_list: # or 'skip_list' to silence them completely STDOUT: .ansible-lint:1: load-failure: [Errno 2] No such file or directory: '~/.cache/ansible-lint-eco/colsystem/tests/ansible-lint.yml' (filenotfounderror) playbooks/molecule/sudo/molecule.yml:17: yaml: line too long (576 > 160 characters) (line-length) +roles/authorized_key/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) +roles/common/meta/main.yml:1: schema: 2.8 is not of type 'string' (schema[meta]) roles/common/tasks/main.yml:1: unnamed-task: All tasks should be named. roles/common/tasks/main.yml:33: unnamed-task: All tasks should be named. +roles/container/meta/main.yml:1: schema: 8 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) roles/container/tasks/main.yml:26: unnamed-task: All tasks should be named. +roles/copy_or_link/meta/main.yml:1: schema: 2.4 is not of type 'string' (schema[meta]) +roles/dev/meta/main.yml:1: schema: 2.4 is not of type 'string' (schema[meta]) +roles/dotfiles/meta/main.yml:1: schema: 2.8 is not of type 'string' (schema[meta]) +roles/epel/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) roles/firewalld/defaults/main.yml:11: yaml: missing starting space in comment (comments) roles/firewalld/defaults/main.yml:28: yaml: missing starting space in comment (comments) +roles/firewalld/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) +roles/flatpak/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) roles/flatpak/tasks/main.yml:1: unnamed-task: All tasks should be named. +roles/k3s_base/meta/main.yml:1: schema: 2.8 is not of type 'string' (schema[meta]) roles/k3s_base/tasks/main.yml:13: unnamed-task: All tasks should be named. +roles/k3s_master/meta/main.yml:1: schema: 2.8 is not of type 'string' (schema[meta]) roles/k3s_master/tasks/main.yml:13: unnamed-task: All tasks should be named. +roles/k3s_worker/meta/main.yml:1: schema: 2.8 is not of type 'string' (schema[meta]) roles/k3s_worker/tasks/main.yml:13: unnamed-task: All tasks should be named. +roles/mariadb/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) roles/mariadb/tasks/secure.yml:1: unnamed-task: All tasks should be named. +roles/mirror/meta/main.yml:1: schema: 2.8 is not of type 'string' (schema[meta]) roles/mirror/tasks/mirror.yml:1: unnamed-task: All tasks should be named. +roles/mounts/meta/main.yml:1: schema: 2.4 is not of type 'string' (schema[meta]) +roles/mta/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) +roles/mythtv/meta/main.yml:1: schema: 2.4 is not of type 'string' (schema[meta]) +roles/packages_server/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) +roles/packages_workstation/meta/main.yml:1: schema: 2.4 is not of type 'string' (schema[meta]) roles/packages_workstation/tasks/Linux/03_libvirt.yml:1: unnamed-task: All tasks should be named. roles/packages_workstation/tasks/Linux/03_libvirt.yml:62: unnamed-task: All tasks should be named. roles/packages_workstation/tasks/Linux/06_blu_ray.yml:18: no-handler: Tasks that run when changed should likely be handlers. roles/packages_workstation/tasks/Linux/06_blu_ray.yml:18: unnamed-task: All tasks should be named. roles/packages_workstation/tasks/Linux/main.yml:11: unnamed-task: All tasks should be named. +roles/python_prep/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) +roles/sudoers/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) roles/sudoers/tasks/main.yml:13: unnamed-task: All tasks should be named. +roles/system_repositories/meta/main.yml:1: schema: 5 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) +roles/update_ca_trust/meta/main.yml:1: schema: 7 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) roles/update_ca_trust/tasks/main.yml:13: unnamed-task: All tasks should be named. +roles/webserver/meta/main.yml:1: schema: 2.8 is not of type 'string' (schema[meta]) diff --git a/test/eco/debops.result b/test/eco/debops.result index 0758807caf..2c762e592e 100644 --- a/test/eco/debops.result +++ b/test/eco/debops.result @@ -4,23 +4,30 @@ RC: 2 STDERR: WARNING Loading custom .yamllint config file, this extends our internal yamllint config. -WARNING Listing 10 violation(s) that are fatal +WARNING Listing 16 violation(s) that are fatal You can skip specific rules or tags by adding them to your configuration file: # .config/ansible-lint.yml warn_list: # or 'skip_list' to silence them completely + - experimental # all rules tagged as experimental - unnamed-task # All tasks should be named. - var-spacing # Variables should have spaces before and after: {{ var_name }}. STDOUT: +ansible/roles/boxbackup/meta/main.yml:1: schema: Additional properties are not allowed ('pki_authorities', 'pki_certificates', 'pki_private_groups_present', 'pki_realms', 'pki_routes', 'when' were unexpected) (schema[meta]) ansible/roles/hashicorp/tasks/main.yml:99: unnamed-task: All tasks should be named. +ansible/roles/lvm/vars/lvm_config_2.02.168.yml:1: schema: 'global' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[\\w]*$' (schema[vars]) +ansible/roles/lvm/vars/lvm_config_debian_stretch.yml:1: schema: 'global' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[\\w]*$' (schema[vars]) ansible/roles/owncloud/tasks/tarball.yml:46: unnamed-task: All tasks should be named. ansible/roles/persistent_paths/tasks/main.yml:12: unnamed-task: All tasks should be named. ansible/roles/pki/tasks/main.yml:201: unnamed-task: All tasks should be named. ansible/roles/pki/tasks/main.yml:403: unnamed-task: All tasks should be named. +ansible/roles/rails_deploy/meta/main.yml:1: schema: 'nodejs' is not of type 'array' (schema[meta]) ansible/roles/rspamd/defaults/main.yml:318: var-spacing: Variables should have spaces before and after: { var_name }. roles/hashicorp/tasks/main.yml:99: unnamed-task: All tasks should be named. +roles/lvm/vars/lvm_config_2.02.168.yml:1: schema: 'global' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[\\w]*$' (schema[vars]) +roles/lvm/vars/lvm_config_debian_stretch.yml:1: schema: 'global' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[\\w]*$' (schema[vars]) roles/owncloud/tasks/tarball.yml:46: unnamed-task: All tasks should be named. roles/persistent_paths/tasks/main.yml:12: unnamed-task: All tasks should be named. roles/rspamd/defaults/main.yml:318: var-spacing: Variables should have spaces before and after: { var_name }. diff --git a/test/eco/docker-rootless.result b/test/eco/docker-rootless.result index 06be1a549d..4f33585c86 100644 --- a/test/eco/docker-rootless.result +++ b/test/eco/docker-rootless.result @@ -4,6 +4,13 @@ RC: 0 STDERR: WARNING Loading custom .yamllint.yml config file, this extends our internal yamllint config. +WARNING Listing 1 violation(s) that are fatal +You can skip specific rules or tags by adding them to your configuration file: +# .config/ansible-lint.yml +warn_list: # or 'skip_list' to silence them completely + - experimental # all rules tagged as experimental + STDOUT: +meta/main.yml:1: schema: 8 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) diff --git a/test/eco/hardening.result b/test/eco/hardening.result index 02a15867a7..db0f112da2 100644 --- a/test/eco/hardening.result +++ b/test/eco/hardening.result @@ -1,15 +1,36 @@ CMD: ansible-lint -f pep8 -x fqcn-builtins -RC: 0 +RC: 2 STDERR: WARNING Loading custom .yamllint.yml config file, this extends our internal yamllint config. -WARNING Listing 4 violation(s) that are fatal +WARNING Listing 5 violation(s) that are fatal +You can skip specific rules or tags by adding them to your configuration file: +# .config/ansible-lint.yml +warn_list: # or 'skip_list' to silence them completely + - experimental # all rules tagged as experimental + - schema # Perform JSON Schema Validation for known lintable kinds. + + Returned errors will not include exact line numbers, but they will mention + the schema name being used as a tag, like ``playbook-schema``, + ``tasks-schema``. + + This rule is not skippable and stops further processing of the file. + + Schema bugs should be reported towards https://github.com/ansible/schemas + project instead of ansible-lint. + + If incorrect schema was picked, you might want to either: + + * move the file to standard location, so its file is detected correctly. + * use ``kinds:`` option in linter config to help it pick correct file type. + STDOUT: defaults/main/sshd.yml:20: yaml: line too long (143 > 120 characters) (line-length) +meta/main.yml:1: schema: 8 is not one of ['6.1', '7.1', '7.2', 'all'] (schema[meta]) tasks/auditd.yml:21: yaml: line too long (122 > 120 characters) (line-length) tasks/packagemgmt.yml:164: yaml: line too long (129 > 120 characters) (line-length) tasks/packagemgmt.yml:192: yaml: line too long (162 > 120 characters) (line-length) diff --git a/test/eco/zuul-jobs.result b/test/eco/zuul-jobs.result index 7c722038da..998d94c98c 100644 --- a/test/eco/zuul-jobs.result +++ b/test/eco/zuul-jobs.result @@ -11,7 +11,7 @@ INFO Discovered files to lint using: git ls-files --cached --others --exclud INFO Excluded removed files using: git ls-files --deleted -z INFO Discovered files to lint using: git ls-files --cached --others --exclude-standard -z INFO Excluded removed files using: git ls-files --deleted -z -WARNING Listing 57 violation(s) that are fatal +WARNING Listing 62 violation(s) that are fatal You can skip specific rules or tags by adding them to your configuration file: # .config/ansible-lint.yml warn_list: # or 'skip_list' to silence them completely @@ -64,10 +64,15 @@ roles/tox/tasks/main.yaml:39: unnamed-task: All tasks should be named. roles/trigger-readthedocs/tasks/main.yaml:11: unnamed-task: All tasks should be named. roles/trigger-readthedocs/tasks/main.yaml:30: unnamed-task: All tasks should be named. roles/upload-git-mirror/tasks/main.yaml:1: unnamed-task: All tasks should be named. +roles/upload-logs-azure/meta/main.yaml:1: schema: {'dependencies': [{'role': 'upload-logs-base'}]} is not of type 'null' (schema[meta]) roles/upload-logs-azure/tasks/main.yaml:7: unnamed-task: All tasks should be named. +roles/upload-logs-gcs/meta/main.yaml:1: schema: {'dependencies': [{'role': 'upload-logs-base'}]} is not of type 'null' (schema[meta]) roles/upload-logs-gcs/tasks/main.yaml:7: unnamed-task: All tasks should be named. +roles/upload-logs-ibm/meta/main.yaml:1: schema: {'dependencies': [{'role': 'upload-logs-base'}]} is not of type 'null' (schema[meta]) roles/upload-logs-ibm/tasks/main.yaml:7: unnamed-task: All tasks should be named. +roles/upload-logs-s3/meta/main.yaml:1: schema: {'dependencies': [{'role': 'upload-logs-base'}]} is not of type 'null' (schema[meta]) roles/upload-logs-s3/tasks/main.yaml:7: unnamed-task: All tasks should be named. +roles/upload-logs-swift/meta/main.yaml:1: schema: {'dependencies': [{'role': 'upload-logs-base'}]} is not of type 'null' (schema[meta]) roles/upload-logs-swift/tasks/main.yaml:7: unnamed-task: All tasks should be named. roles/upload-logs-swift/tasks/main.yaml:33: unnamed-task: All tasks should be named. roles/upload-logs/tasks/main.yaml:6: unnamed-task: All tasks should be named. diff --git a/test/test_examples.py b/test/test_examples.py index df058c2899..0778a77513 100644 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -29,7 +29,7 @@ def test_example(default_rules_collection: RulesCollection) -> None: ("filename", "line", "column"), ( pytest.param( - "examples/playbooks/syntax-error-string.yml", 1, 1, id="syntax-error-string" + "examples/playbooks/syntax-error-string.yml", 6, 7, id="syntax-error" ), pytest.param("examples/playbooks/syntax-error.yml", 2, 3, id="syntax-error"), ), diff --git a/test/test_rules_collection.py b/test/test_rules_collection.py index 634814e0d2..43e7ca73ed 100644 --- a/test/test_rules_collection.py +++ b/test/test_rules_collection.py @@ -159,4 +159,4 @@ def test_rules_id_format() -> None: assert rule_id_re.match( rule.id ), f"Rule id {rule.id} did not match our required format." - assert len(rules) == 40 + assert len(rules) == 41 diff --git a/test/test_skip_inside_yaml.py b/test/test_skip_inside_yaml.py index df47b067ee..1db6875b47 100644 --- a/test/test_skip_inside_yaml.py +++ b/test/test_skip_inside_yaml.py @@ -80,6 +80,8 @@ author: your name # noqa meta-incorrect description: missing min_ansible_version and platforms. author default not changed license: MIT + min_ansible_version: "2.10" + platforms: [] """ ROLE_TASKS_WITH_BLOCK_BECOME = """\ diff --git a/test/test_utils.py b/test/test_utils.py index 59c57e21a3..8e344bb8d9 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -294,6 +294,8 @@ def test_cli_auto_detect(capfd: CaptureFixture[str]) -> None: sys.executable, "-m", "ansiblelint", + "-x", + "schema", # exclude schema as our test file would fail it "-v", "-p", "--nocolor",