From dc8c9c0e9e87a04e177a1c3a1f72582b38d3ee0e Mon Sep 17 00:00:00 2001 From: Ajinkya Udgirkar Date: Wed, 18 Jan 2023 16:42:28 +0530 Subject: [PATCH 01/36] Added tests for text.py (#2902) Co-authored-by: Ajinkya Udgirkar Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/tox.yml | 2 +- test/test_text.py | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 8b335b7b14..3b425f02d4 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -69,7 +69,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 749 + PYTEST_REQPASS: 755 steps: - name: Activate WSL1 diff --git a/test/test_text.py b/test/test_text.py index 0dbee95dec..fa91feeb7d 100644 --- a/test/test_text.py +++ b/test/test_text.py @@ -3,7 +3,43 @@ import pytest -from ansiblelint.text import has_glob, has_jinja +from ansiblelint.text import has_glob, has_jinja, strip_ansi_escape, toidentifier + + +@pytest.mark.parametrize( + ("value", "expected"), + ( + pytest.param("\x1b[1;31mHello", "Hello", id="0"), + pytest.param("\x1b[2;37;41mExample_file.zip", "Example_file.zip", id="1"), + pytest.param(b"ansible-lint", "ansible-lint", id="2"), + ), +) +def test_strip_ansi_escape(value: Any, expected: str) -> None: + """Tests for strip_ansi_escape().""" + assert strip_ansi_escape(value) == expected + + +@pytest.mark.parametrize( + ("value", "expected"), + ( + pytest.param("foo-bar", "foo_bar", id="0"), + pytest.param("foo--bar", "foo_bar", id="1"), + ), +) +def test_toidentifier(value: Any, expected: str) -> None: + """Tests for toidentifier().""" + assert toidentifier(value) == expected + + +@pytest.mark.parametrize( + ("value", "expected"), + (pytest.param("example_test.zip", "Unable to convert role name", id="0"),), +) +def test_toidentifier_fail(value: Any, expected: str) -> None: + """Tests for toidentifier().""" + with pytest.raises(RuntimeError) as err: + toidentifier(value) + assert str(err.value).find(expected) > -1 @pytest.mark.parametrize( From 6accf29cc749cfb6b3967b85927d37d18094a9bd Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 18 Jan 2023 11:29:28 +0000 Subject: [PATCH 02/36] Configure vscode to reformat on save (#2911) --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 35361626f0..812cd4f95d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "[markdown]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "editor.formatOnSave": true, "evenBetterToml.formatter.allowedBlankLines": 2, "files.exclude": { "*.egg-info": true, @@ -10,7 +11,6 @@ "__pycache__": true, "build": true }, - "python.analysis.exclude": ["build"], "git.ignoreLimitWarning": true, "grammarly.domain": "technical", "grammarly.files.include": [ @@ -19,6 +19,9 @@ ], "grammarly.hideUnavailablePremiumAlerts": true, "grammarly.showExamples": true, + "python.analysis.exclude": [ + "build" + ], "python.formatting.provider": "black", "python.linting.flake8Enabled": true, "python.linting.mypyCategorySeverity.error": "Warning", From 5e04de23292c5f90d858fdb5cd50a5f9c8e710ff Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 18 Jan 2023 11:29:54 +0000 Subject: [PATCH 03/36] Fix galaxy[no-changelog] testing (#2910) --- examples/no_changelog/galaxy.yml | 4 ++-- src/ansiblelint/rules/galaxy.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/no_changelog/galaxy.yml b/examples/no_changelog/galaxy.yml index 8caac51764..726c4ccd3b 100644 --- a/examples/no_changelog/galaxy.yml +++ b/examples/no_changelog/galaxy.yml @@ -11,6 +11,6 @@ dependencies: other_namespace.collection2: ">=2.0.0,<3.0.0" anderson55.my_collection: "*" # note: "*" selects the highest version available license: - - GPL # <-- invalid license values based on galaxy schema - - Apache + - GPL-3.0-or-later + - Apache-2.0 repository: some-url diff --git a/src/ansiblelint/rules/galaxy.py b/src/ansiblelint/rules/galaxy.py index 8a95cd1e3a..168ab1a6a8 100644 --- a/src/ansiblelint/rules/galaxy.py +++ b/src/ansiblelint/rules/galaxy.py @@ -147,5 +147,7 @@ def test_changelog_missing() -> None: collection = RulesCollection() collection.register(GalaxyRule()) bad_runner = Runner("examples/no_changelog/galaxy.yml", rules=collection) - errs = bad_runner.run() - assert len(errs) == 1 + result = bad_runner.run() + assert len(result) == 1 + for item in result: + assert item.tag == "galaxy[no-changelog]" From 5a5a47f39447baf620d8397372a45e670d8ea127 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 18 Jan 2023 16:06:18 +0000 Subject: [PATCH 04/36] Fix print leak with meta-unsupported-ansible (#2913) - also removes several other undesired prints - adds preventive measure (flake8-print) Fixes: #2908 --- .pre-commit-config.yaml | 1 + docs/conf.py | 1 + src/ansiblelint/__main__.py | 2 ++ src/ansiblelint/rules/ignore_errors.py | 1 - src/ansiblelint/rules/meta_unsupported_ansible.py | 2 -- src/ansiblelint/rules/yaml_rule.py | 1 - src/ansiblelint/schemas/__main__.py | 2 ++ src/ansiblelint/schemas/main.py | 2 ++ test/fixtures/test_regenerate_formatting_fixtures.py | 10 ++++------ test/schemas/src/rebuild.py | 2 ++ test/test_list_rules.py | 1 - 11 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3cd13bb307..35ee91f36b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -147,6 +147,7 @@ repos: - flake8-2020>=1.6.0 # - flake8-black>=0.1.1 - flake8-docstrings>=1.5.0 + - flake8-print>=5.0 - flake8-pytest-style>=1.2.2 - flake8-future-annotations>=0.0.3 - repo: https://github.com/asottile/pyupgrade diff --git a/docs/conf.py b/docs/conf.py index 5a4828f430..98eee82896 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,6 +62,7 @@ # Fail safe protection to detect conflicting packages try: pkg_resources.get_distribution("sphinxcontrib-programoutput") + # flake8: noqa: T201 print( "FATAL: We detected presence of sphinxcontrib-programoutput package instead of sphinxcontrib-programoutput2 one. You must be sure the first is not installed.", file=sys.stderr, diff --git a/src/ansiblelint/__main__.py b/src/ansiblelint/__main__.py index e40151430b..fd618a2fac 100755 --- a/src/ansiblelint/__main__.py +++ b/src/ansiblelint/__main__.py @@ -369,6 +369,7 @@ def path_inject() -> None: paths[idx] = os.path.expanduser(path) expanded = True if expanded: # pragma: no cover + # flake8: noqa: T201 print( "WARNING: PATH altered to expand ~ in it. Read https://stackoverflow.com/a/44704799/99834 and correct your system configuration.", file=sys.stderr, @@ -387,6 +388,7 @@ def path_inject() -> None: inject_paths.append(py_path) if inject_paths: + # flake8: noqa: T201 print( f"WARNING: PATH altered to include {', '.join(inject_paths)} :: This is usually a sign of broken local setup, which can cause unexpected behaviors.", file=sys.stderr, diff --git a/src/ansiblelint/rules/ignore_errors.py b/src/ansiblelint/rules/ignore_errors.py index 7468658c1d..f2d84d9b46 100644 --- a/src/ansiblelint/rules/ignore_errors.py +++ b/src/ansiblelint/rules/ignore_errors.py @@ -111,7 +111,6 @@ def test_ignore_errors_false(rule_runner: RunFromText) -> None: def test_ignore_errors_check_mode(rule_runner: RunFromText) -> None: """The task uses ignore_errors: "{{ ansible_check_mode }}".""" results = rule_runner.run_playbook(IGNORE_ERRORS_CHECK_MODE) - print(results) assert len(results) == 0 @pytest.mark.parametrize( diff --git a/src/ansiblelint/rules/meta_unsupported_ansible.py b/src/ansiblelint/rules/meta_unsupported_ansible.py index 944f9cb236..9dd584c5dd 100644 --- a/src/ansiblelint/rules/meta_unsupported_ansible.py +++ b/src/ansiblelint/rules/meta_unsupported_ansible.py @@ -43,8 +43,6 @@ def matchyaml(self, file: Lintable) -> list[MatchError]: version_required = file.data.get("requires_ansible", None) - print(version_required) - if version_required: if not any( version in version_required for version in self.supported_ansible diff --git a/src/ansiblelint/rules/yaml_rule.py b/src/ansiblelint/rules/yaml_rule.py index 8f35ca8280..d731fc0d2a 100644 --- a/src/ansiblelint/rules/yaml_rule.py +++ b/src/ansiblelint/rules/yaml_rule.py @@ -172,7 +172,6 @@ def test_yamllint_has_help(default_rules_collection: RulesCollection) -> None: """Asserts that we loaded markdown documentation in help property.""" for collection in default_rules_collection: if collection.id == "yaml": - print(collection.id) assert collection.help is not None assert len(collection.help) > 100 break diff --git a/src/ansiblelint/schemas/__main__.py b/src/ansiblelint/schemas/__main__.py index 7633a56490..7d18417ed4 100644 --- a/src/ansiblelint/schemas/__main__.py +++ b/src/ansiblelint/schemas/__main__.py @@ -7,7 +7,9 @@ if __name__ == "__main__": if refresh_schemas(): + # flake8: noqa: T201 print("Schemas were updated.") sys.exit(1) else: + # flake8: noqa: T201 print("Schemas not updated", 0) diff --git a/src/ansiblelint/schemas/main.py b/src/ansiblelint/schemas/main.py index 4339b6c81f..81aab82598 100644 --- a/src/ansiblelint/schemas/main.py +++ b/src/ansiblelint/schemas/main.py @@ -146,7 +146,9 @@ def refresh_schemas(min_age_seconds: int = 3600 * 24) -> int: if __name__ == "__main__": if refresh_schemas(): + # flake8: noqa: T201 print("Schemas were updated.") sys.exit(1) else: + # flake8: noqa: T201 print("Schemas not updated", 0) diff --git a/test/fixtures/test_regenerate_formatting_fixtures.py b/test/fixtures/test_regenerate_formatting_fixtures.py index 9a487f2499..b8ac76f801 100644 --- a/test/fixtures/test_regenerate_formatting_fixtures.py +++ b/test/fixtures/test_regenerate_formatting_fixtures.py @@ -17,7 +17,6 @@ def test_regenerate_formatting_fixtures() -> None: we run prettier again to make sure it does not change files formatted with our internal formatting code. """ - print("Looking for prettier on PATH...") subprocess.check_call(["which", "prettier"]) yaml = FormattedYAML() @@ -30,21 +29,20 @@ def test_regenerate_formatting_fixtures() -> None: fixtures_dir_prettier.mkdir(exist_ok=True) fixtures_dir_after.mkdir(exist_ok=True) - print("\nCopying before fixtures...") + # Copying before fixtures... for fixture in fixtures_dir_before.glob("fmt-[0-9].yml"): shutil.copy(str(fixture), str(fixtures_dir_prettier / fixture.name)) shutil.copy(str(fixture), str(fixtures_dir_after / fixture.name)) - print("\nWriting fixtures with prettier...") + # Writing fixtures with prettier... subprocess.check_call(["prettier", "-w", str(fixtures_dir_prettier)]) # NB: pre-commit end-of-file-fixer can also modify files. - print("\nWriting fixtures with ansiblelint.yaml_utils.FormattedYAML()...") + # Writing fixtures with ansiblelint.yaml_utils.FormattedYAML() for fixture in fixtures_dir_after.glob("fmt-[0-9].yml"): data = yaml.loads(fixture.read_text()) output = yaml.dumps(data) - print(fixture) fixture.write_text(output) - print(f"\nMake sure prettier won't make changes in {fixtures_dir_after}...") + # Make sure prettier won't make changes in {fixtures_dir_after} subprocess.check_call(["prettier", "-c", str(fixtures_dir_after)]) diff --git a/test/schemas/src/rebuild.py b/test/schemas/src/rebuild.py index b161b8a65b..0ac0e78894 100644 --- a/test/schemas/src/rebuild.py +++ b/test/schemas/src/rebuild.py @@ -78,6 +78,7 @@ def is_ref_used(obj: Any, ref: str) -> bool: invalid_var_names = sorted(list(keyword.kwlist) + play_keywords) if "__peg_parser__" in invalid_var_names: invalid_var_names.remove("__peg_parser__") + # flake8: noqa: T201 print("Updating invalid var names") with open("f/vars.json", "r+", encoding="utf-8") as f: @@ -90,6 +91,7 @@ def is_ref_used(obj: Any, ref: str) -> bool: f.write("\n") f.truncate() + # flake8: noqa: T201 print("Compiling subschemas...") with open("f/ansible.json", encoding="utf-8") as f: combined_json = json.load(f) diff --git a/test/test_list_rules.py b/test/test_list_rules.py index d5280171c3..a72c04fc34 100644 --- a/test/test_list_rules.py +++ b/test/test_list_rules.py @@ -56,7 +56,6 @@ def test_list_rules_with_format_option( fakerole = os.path.join("test", "fixtures", "list-rules-tests") result_list_rules = run_ansible_lint("-f", format_string, "-L", fakerole, cwd=cwd) - print(result_list_rules.stdout) assert (f"invalid choice: '{format_string}'" in result_list_rules.stderr) is result assert ("syntax-check" in result_list_rules.stdout) is not result From ef130ce37d4ef9e877b3d7f28ba85f893b479fb8 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 18 Jan 2023 16:53:18 +0000 Subject: [PATCH 05/36] Fix github archives metadata (#2914) Fixes: #2909 --- .git_archival.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git_archival.txt b/.git_archival.txt index 95cb3eea4e..3994ec0a83 100644 --- a/.git_archival.txt +++ b/.git_archival.txt @@ -1 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true)$ ref-names: $Format:%D$ From 48629ad3a5bbdb0d1e244b9a77550f0407b28802 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Thu, 19 Jan 2023 17:53:49 +0000 Subject: [PATCH 06/36] Change treatment of files that fail to load structured data (#2919) --- src/ansiblelint/_internal/rules.py | 4 +-- src/ansiblelint/constants.py | 13 ++++++++ src/ansiblelint/file_utils.py | 50 +++++++++++++++++++----------- src/ansiblelint/runner.py | 9 +++--- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/ansiblelint/_internal/rules.py b/src/ansiblelint/_internal/rules.py index bb631f6de4..4d0bf4930b 100644 --- a/src/ansiblelint/_internal/rules.py +++ b/src/ansiblelint/_internal/rules.py @@ -17,7 +17,7 @@ LOAD_FAILURE_MD = """\ # load-failure -Linter failed to process a YAML file, probably because it is either: +"Linter failed to process a file, possible invalid file. Possible reasons: * contains unsupported encoding (only UTF-8 is supported) * not an Ansible file @@ -175,7 +175,7 @@ class LoadingFailureRule(BaseRule): """Failed to load or parse file.""" id = "load-failure" - description = "Linter failed to process a YAML file, possible not an Ansible file." + description = "Linter failed to process a file, possible invalid file." severity = "VERY_HIGH" tags = ["core", "unskippable"] version_added = "v4.3.0" diff --git a/src/ansiblelint/constants.py b/src/ansiblelint/constants.py index 48c0d19b6a..0ec7c7a269 100644 --- a/src/ansiblelint/constants.py +++ b/src/ansiblelint/constants.py @@ -1,5 +1,6 @@ """Constants used by AnsibleLint.""" import os.path +from enum import Enum from typing import Literal DEFAULT_RULESDIR = os.path.join(os.path.dirname(__file__), "rules") @@ -152,3 +153,15 @@ def main(): # # https://github.com/ansible/ansible-lint-action/issues/138 GIT_CMD = ["git", "-c", f"safe.directory={os.getcwd()}"] + + +class States(Enum): + """States used are used as sentinel values in various places.""" + + NOT_LOADED = "File not loaded" + LOAD_FAILED = "File failed to load" + UNKNOWN_DATA = "Unknown data" + + def __bool__(self) -> bool: + """Ensure all states evaluate as False as booleans.""" + return False diff --git a/src/ansiblelint/file_utils.py b/src/ansiblelint/file_utils.py index 041a988a76..e5c7a59302 100644 --- a/src/ansiblelint/file_utils.py +++ b/src/ansiblelint/file_utils.py @@ -14,12 +14,12 @@ from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING, Any, Iterator, cast -# import wcmatch import wcmatch.pathlib from wcmatch.wcmatch import RECURSIVE, WcMatch +from yaml.error import YAMLError from ansiblelint.config import BASE_KINDS, options -from ansiblelint.constants import GIT_CMD, FileType +from ansiblelint.constants import GIT_CMD, FileType, States if TYPE_CHECKING: # https://github.com/PyCQA/pylint/issues/3979 @@ -189,8 +189,9 @@ def __init__( self.dir: str = "" self.kind: FileType | None = None self.stop_processing = False # Set to stop other rules from running - self._data: Any = None + self._data: Any = States.NOT_LOADED self.line_skips: dict[int, set[str]] = defaultdict(set) + self.exc: Exception | None = None # Stores data loading exceptions if isinstance(name, str): name = Path(name) @@ -341,21 +342,34 @@ def __repr__(self) -> str: @property def data(self) -> Any: """Return loaded data representation for current file, if possible.""" - if not self._data: - if str(self.base_kind) == "text/yaml": - from ansiblelint.utils import ( # pylint: disable=import-outside-toplevel - parse_yaml_linenumbers, - ) - - self._data = parse_yaml_linenumbers(self) - # Lazy import to avoid delays and cyclic-imports - if "append_skipped_rules" not in globals(): - # pylint: disable=import-outside-toplevel - from ansiblelint.skip_utils import append_skipped_rules - - self._data = append_skipped_rules(self._data, self) - - # will remain None if we do not know how to load that file-type + if self._data == States.NOT_LOADED: + if self.path.is_dir(): + self._data = None + return self._data + try: + if str(self.base_kind) == "text/yaml": + from ansiblelint.utils import ( # pylint: disable=import-outside-toplevel + parse_yaml_linenumbers, + ) + + self._data = parse_yaml_linenumbers(self) + # Lazy import to avoid delays and cyclic-imports + if "append_skipped_rules" not in globals(): + # pylint: disable=import-outside-toplevel + from ansiblelint.skip_utils import append_skipped_rules + + self._data = append_skipped_rules(self._data, self) + else: + logging.debug( + "data set to None for %s due to being of %s kind.", + self.path, + self.base_kind, + ) + self._data = States.UNKNOWN_DATA + + except (RuntimeError, FileNotFoundError, YAMLError) as exc: + self._data = States.LOAD_FAILED + self.exc = exc return self._data diff --git a/src/ansiblelint/runner.py b/src/ansiblelint/runner.py index 2b220ebdcf..3145699b0c 100644 --- a/src/ansiblelint/runner.py +++ b/src/ansiblelint/runner.py @@ -12,6 +12,7 @@ import ansiblelint.skip_utils import ansiblelint.utils from ansiblelint._internal.rules import LoadingFailureRule +from ansiblelint.constants import States from ansiblelint.errors import MatchError from ansiblelint.file_utils import Lintable, expand_dirs_in_lintables from ansiblelint.rules.syntax_check import AnsibleSyntaxCheckRule @@ -122,14 +123,12 @@ def run(self) -> list[MatchError]: # noqa: C901 _logger.debug("Excluded %s", lintable) self.lintables.remove(lintable) continue - try: - lintable.data - except (RuntimeError, FileNotFoundError) as exc: + if isinstance(lintable.data, States) and lintable.exc: matches.append( MatchError( filename=lintable, - message=str(exc), - details=str(exc.__cause__), + message=str(lintable.exc), + details=str(lintable.exc.__cause__), rule=LoadingFailureRule(), ) ) From 497bae917df7739eae885b219440e4be0ee31bcb Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Thu, 19 Jan 2023 19:37:14 +0000 Subject: [PATCH 07/36] Determine if passed arguments are playbooks or not (#2912) From now on, ansible-lint will no longer assume that a passed argument must be a playbook. This addressed multiple bug reports where people were confused that the linter reported errors when they passed a taskfile as an argument. The downside is that an invalid playbook that is a valid YAML file might not raise an error and just report a warning and be treated as a generic yaml file. Fixes: #2892 --- .config/dictionary.txt | 2 + .github/workflows/tox.yml | 2 +- examples/.config/molecule/config.yml | 0 examples/other/guess-1.yml | 3 + examples/playbooks/rulebook.yml | 21 +++ examples/roles/foo.yml | 0 examples/rulebooks/rulebook.yml | 19 +++ src/ansiblelint/config.py | 1 + src/ansiblelint/constants.py | 1 + src/ansiblelint/file_utils.py | 18 +++ src/ansiblelint/rules/name.py | 2 - src/ansiblelint/schemas/main.py | 1 - src/ansiblelint/utils.py | 8 -- test/local-content/test-collection.yml | 2 +- test/schemas/test/molecule/cluster/base.yml | 0 .../test/molecule/cluster/converge.yml | 0 test/schemas/test/molecule/cluster/foobar.yml | 0 test/test_file_utils.py | 125 +++++++++++------- test/test_formatter.py | 6 +- test/test_formatter_json.py | 6 +- test/test_formatter_sarif.py | 4 +- test/test_matcherrror.py | 4 +- test/test_yaml_utils.py | 3 +- 23 files changed, 155 insertions(+), 73 deletions(-) create mode 100644 examples/.config/molecule/config.yml create mode 100644 examples/other/guess-1.yml create mode 100644 examples/playbooks/rulebook.yml create mode 100644 examples/roles/foo.yml create mode 100644 examples/rulebooks/rulebook.yml create mode 100644 test/schemas/test/molecule/cluster/base.yml create mode 100644 test/schemas/test/molecule/cluster/converge.yml create mode 100644 test/schemas/test/molecule/cluster/foobar.yml diff --git a/.config/dictionary.txt b/.config/dictionary.txt index 895fb4cc41..1b633b2bca 100644 --- a/.config/dictionary.txt +++ b/.config/dictionary.txt @@ -270,6 +270,8 @@ robertdebock rolepath roundtrip ruamel +rulebook +rulebooks ruledirs rulesdir rulesdirs diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 3b425f02d4..8c03b4925b 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -69,7 +69,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 755 + PYTEST_REQPASS: 767 steps: - name: Activate WSL1 diff --git a/examples/.config/molecule/config.yml b/examples/.config/molecule/config.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/other/guess-1.yml b/examples/other/guess-1.yml new file mode 100644 index 0000000000..b11fb9c9d8 --- /dev/null +++ b/examples/other/guess-1.yml @@ -0,0 +1,3 @@ +--- +- name: Minimal yaml to determine it as a playbook + hosts: localhost diff --git a/examples/playbooks/rulebook.yml b/examples/playbooks/rulebook.yml new file mode 100644 index 0000000000..3eaf308928 --- /dev/null +++ b/examples/playbooks/rulebook.yml @@ -0,0 +1,21 @@ +--- +# That file is not a valid playbook but it is a valid rulebook that was +# mistakenly put under a playbook directory. +- name: Demo rules with kafka as source + hosts: localhost + sources: + - name: kafka + kafka: + topic: eda + host: localhost + port: 9092 + group_id: testing + rules: + - name: + condition: event.i is defined + action: + debug: + - name: + condition: event.stop == true + action: + shutdown: diff --git a/examples/roles/foo.yml b/examples/roles/foo.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/rulebooks/rulebook.yml b/examples/rulebooks/rulebook.yml new file mode 100644 index 0000000000..1f5d444144 --- /dev/null +++ b/examples/rulebooks/rulebook.yml @@ -0,0 +1,19 @@ +--- +- name: Demo rules with kafka as source + hosts: localhost + sources: + - name: kafka + kafka: + topic: eda + host: localhost + port: 9092 + group_id: testing + rules: + - name: + condition: event.i is defined + action: + debug: + - name: + condition: event.stop == true + action: + shutdown: diff --git a/src/ansiblelint/config.py b/src/ansiblelint/config.py index 7b5d1866de..c86275d0ca 100644 --- a/src/ansiblelint/config.py +++ b/src/ansiblelint/config.py @@ -56,6 +56,7 @@ {"galaxy": "**/galaxy.yml"}, # Galaxy collection meta {"reno": "**/releasenotes/*/*.{yaml,yml}"}, # reno release notes {"tasks": "**/tasks/**/*.{yaml,yml}"}, + {"rulebook": "**/rulebooks/*.{yml,yaml"}, {"playbook": "**/playbooks/*.{yml,yaml}"}, {"playbook": "**/*playbook*.{yml,yaml}"}, {"role": "**/roles/*/"}, diff --git a/src/ansiblelint/constants.py b/src/ansiblelint/constants.py index 0ec7c7a269..0fec8fdd6a 100644 --- a/src/ansiblelint/constants.py +++ b/src/ansiblelint/constants.py @@ -53,6 +53,7 @@ def main(): FileType = Literal[ "playbook", + "rulebook", "meta", # role meta "meta-runtime", "tasks", # includes pre_tasks, post_tasks diff --git a/src/ansiblelint/file_utils.py b/src/ansiblelint/file_utils.py index e5c7a59302..1cba0e8985 100644 --- a/src/ansiblelint/file_utils.py +++ b/src/ansiblelint/file_utils.py @@ -241,6 +241,24 @@ def __init__( self.base_kind = base_kind or kind_from_path(self.path, base=True) self.abspath = self.path.expanduser().absolute() + if self.kind == "yaml": + self._guess_kind() + + def _guess_kind(self) -> None: + if self.kind == "yaml": + if isinstance(self.data, list) and "hosts" in self.data[0]: + if "rules" not in self.data[0]: + self.kind = "playbook" + else: + self.kind = "rulebook" + # we we failed to guess the more specific kind, we warn user + if self.kind == "yaml": + _logger.debug( + "Passed '%s' positional argument was identified as generic '%s' file kind.", + self.name, + self.kind, + ) + def __getitem__(self, key: Any) -> Any: """Provide compatibility subscriptable support.""" if key == "path": diff --git a/src/ansiblelint/rules/name.py b/src/ansiblelint/rules/name.py index f6e82e08bb..ba34f8a3b8 100644 --- a/src/ansiblelint/rules/name.py +++ b/src/ansiblelint/rules/name.py @@ -117,14 +117,12 @@ def _check_name( return results else: effective_name = name[len(prefix) :] - # breakpoint() if ( effective_name[0].isalpha() and effective_name[0].islower() and not effective_name[0].isupper() ): - # breakpoint() results.append( self.create_matcherror( message="All names should start with an uppercase letter.", diff --git a/src/ansiblelint/schemas/main.py b/src/ansiblelint/schemas/main.py index 81aab82598..97f9eb9462 100644 --- a/src/ansiblelint/schemas/main.py +++ b/src/ansiblelint/schemas/main.py @@ -90,7 +90,6 @@ def refresh_schemas(min_age_seconds: int = 3600 * 24) -> int: _logger.debug("Checking for updated schemas...") changed = 0 - # breakpoint() for kind, data in JSON_SCHEMAS.items(): url = data["url"] if "#" in url: diff --git a/src/ansiblelint/utils.py b/src/ansiblelint/utils.py index 4054cab00b..51c8bfbde2 100644 --- a/src/ansiblelint/utils.py +++ b/src/ansiblelint/utils.py @@ -846,14 +846,6 @@ def get_lintables( if args: for arg in args: lintable = Lintable(arg) - if lintable.kind in ("yaml", None): - _logger.warning( - "Overriding detected file kind '%s' with 'playbook' " - "for given positional argument: %s", - lintable.kind, - arg, - ) - lintable = Lintable(arg, kind="playbook") lintables.append(lintable) else: diff --git a/test/local-content/test-collection.yml b/test/local-content/test-collection.yml index 0de3b195e3..47b097d51f 100644 --- a/test/local-content/test-collection.yml +++ b/test/local-content/test-collection.yml @@ -5,6 +5,6 @@ - name: Use module from local collection testns.test_collection.test_module_2: - name: Use filter from local collection - assert: + ansible.builtin.assert: that: - 1 | testns.test_collection.test_filter(2) == '1:2' diff --git a/test/schemas/test/molecule/cluster/base.yml b/test/schemas/test/molecule/cluster/base.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/schemas/test/molecule/cluster/converge.yml b/test/schemas/test/molecule/cluster/converge.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/schemas/test/molecule/cluster/foobar.yml b/test/schemas/test/molecule/cluster/foobar.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/test_file_utils.py b/test/test_file_utils.py index 8a75e271de..b427fa833a 100644 --- a/test/test_file_utils.py +++ b/test/test_file_utils.py @@ -144,60 +144,94 @@ def test_discover_lintables_umlaut(monkeypatch: MonkeyPatch) -> None: @pytest.mark.parametrize( ("path", "kind"), ( - ("tasks/run_test_playbook.yml", "tasks"), - ("foo/playbook.yml", "playbook"), - ("playbooks/foo.yml", "playbook"), - ("playbooks/roles/foo.yml", "yaml"), + pytest.param("tasks/run_test_playbook.yml", "tasks", id="0"), + pytest.param("foo/playbook.yml", "playbook", id="1"), + pytest.param("playbooks/foo.yml", "playbook", id="2"), + pytest.param("examples/roles/foo.yml", "yaml", id="3"), # the only yml file that is not a playbook inside molecule/ folders - (".config/molecule/config.yml", "yaml"), # molecule shared config - ( - "roles/foo/molecule/scenario1/base.yml", - "yaml", + pytest.param( + "examples/.config/molecule/config.yml", "yaml", id="4" + ), # molecule shared config + pytest.param( + "test/schemas/test/molecule/cluster/base.yml", "yaml", id="5" ), # molecule scenario base config - ( - "roles/foo/molecule/scenario1/molecule.yml", - "yaml", + pytest.param( + "test/schemas/test/molecule/cluster/molecule.yml", "yaml", id="6" ), # molecule scenario config - ("roles/foo/molecule/scenario2/foobar.yml", "playbook"), # custom playbook name - ( - "roles/foo/molecule/scenario3/converge.yml", - "playbook", + pytest.param( + "test/schemas/test/molecule/cluster/foobar.yml", "playbook", id="7" + ), # custom playbook name + pytest.param( + "test/schemas/test/molecule/cluster/converge.yml", "playbook", id="8" ), # common playbook name - ( - "roles/foo/molecule/scenario3/requirements.yml", - "requirements", + pytest.param( + "roles/foo/molecule/scenario3/requirements.yml", "requirements", id="9" ), # requirements - ( - "roles/foo/molecule/scenario3/collections.yml", - "requirements", + pytest.param( + "roles/foo/molecule/scenario3/collections.yml", "requirements", id="10" ), # requirements - ("roles/foo/meta/argument_specs.yml", "arg_specs"), # role argument specs + pytest.param( + "roles/foo/meta/argument_specs.yml", "arg_specs", id="11" + ), # role argument specs # tasks files: - ("tasks/directory with spaces/main.yml", "tasks"), # tasks - ("tasks/requirements.yml", "tasks"), # tasks + pytest.param("tasks/directory with spaces/main.yml", "tasks", id="12"), # tasks + pytest.param("tasks/requirements.yml", "tasks", id="13"), # tasks # requirements (we do not support includes yet) - ("requirements.yml", "requirements"), # collection requirements - ("roles/foo/meta/requirements.yml", "requirements"), # inside role requirements + pytest.param( + "requirements.yml", "requirements", id="14" + ), # collection requirements + pytest.param( + "roles/foo/meta/requirements.yml", "requirements", id="15" + ), # inside role requirements # Undeterminable files: - ("test/fixtures/unknown-type.yml", "yaml"), - ("releasenotes/notes/run-playbooks-refactor.yaml", "reno"), # reno - ("examples/host_vars/localhost.yml", "vars"), - ("examples/group_vars/all.yml", "vars"), - ("examples/playbooks/vars/other.yml", "vars"), - ("examples/playbooks/vars/subfolder/settings.yml", "vars"), # deep vars - ("molecule/scenario/collections.yml", "requirements"), # deprecated 2.8 format - ( - "../roles/geerlingguy.mysql/tasks/configure.yml", - "tasks", + pytest.param("test/fixtures/unknown-type.yml", "yaml", id="16"), + pytest.param( + "releasenotes/notes/run-playbooks-refactor.yaml", "reno", id="17" + ), # reno + pytest.param("examples/host_vars/localhost.yml", "vars", id="18"), + pytest.param("examples/group_vars/all.yml", "vars", id="19"), + pytest.param("examples/playbooks/vars/other.yml", "vars", id="20"), + pytest.param( + "examples/playbooks/vars/subfolder/settings.yml", "vars", id="21" + ), # deep vars + pytest.param( + "molecule/scenario/collections.yml", "requirements", id="22" + ), # deprecated 2.8 format + pytest.param( + "../roles/geerlingguy.mysql/tasks/configure.yml", "tasks", id="23" ), # relative path involved - ("galaxy.yml", "galaxy"), - ("foo.j2.yml", "jinja2"), - ("foo.yml.j2", "jinja2"), - ("foo.j2.yaml", "jinja2"), - ("foo.yaml.j2", "jinja2"), + pytest.param("galaxy.yml", "galaxy", id="24"), + pytest.param("foo.j2.yml", "jinja2", id="25"), + pytest.param("foo.yml.j2", "jinja2", id="26"), + pytest.param("foo.j2.yaml", "jinja2", id="27"), + pytest.param("foo.yaml.j2", "jinja2", id="28"), + pytest.param( + "examples/playbooks/rulebook.yml", "playbook", id="29" + ), # playbooks folder should determine kind + pytest.param( + "examples/rulebooks/rulebook.yml", "rulebook", id="30" + ), # content should determine it as a rulebook + pytest.param( + "examples/yamllint/valid.yml", "yaml", id="31" + ), # empty yaml is valid yaml, not assuming anything else + pytest.param( + "examples/other/guess-1.yml", "playbook", id="32" + ), # content should determine is as a play + pytest.param( + "examples/playbooks/tasks/passing_task.yml", "tasks", id="33" + ), # content should determine is tasks + pytest.param("examples/collection/galaxy.yml", "galaxy", id="34"), + pytest.param("examples/meta/runtime.yml", "meta-runtime", id="35"), + pytest.param("examples/meta/changelogs/changelog.yaml", "changelog", id="36"), + pytest.param("examples/inventory/inventory.yml", "inventory", id="37"), + pytest.param("examples/inventory/production.yml", "inventory", id="38"), + pytest.param("examples/playbooks/vars/empty_vars.yml", "vars", id="39"), + pytest.param( + "examples/playbooks/vars/subfolder/settings.yaml", "vars", id="40" + ), ), ) -def test_default_kinds(monkeypatch: MonkeyPatch, path: str, kind: FileType) -> None: +def test_kinds(monkeypatch: MonkeyPatch, path: str, kind: FileType) -> None: """Verify auto-detection logic based on DEFAULT_KINDS.""" options = cli.get_config([]) @@ -212,9 +246,6 @@ def mockreturn(options: Namespace) -> dict[str, Any]: monkeypatch.setattr(file_utils, "discover_lintables", mockreturn) result = file_utils.discover_lintables(options) - # get_lintable could return additional files and we only care to see - # that the given file is among the returned list. - assert lintable_detected.name in result assert lintable_detected.kind == result[lintable_expected.name] @@ -310,9 +341,7 @@ def test_lintable_with_new_file(tmp_path: Path) -> None: """Validate ``Lintable.updated`` for a new file.""" lintable = Lintable(tmp_path / "new.yaml") - with pytest.raises(FileNotFoundError): - _ = lintable.content - + lintable.content = BASIC_PLAYBOOK lintable.content = BASIC_PLAYBOOK assert lintable.content == BASIC_PLAYBOOK diff --git a/test/test_formatter.py b/test/test_formatter.py index 964dbe940a..29153f852b 100644 --- a/test/test_formatter.py +++ b/test/test_formatter.py @@ -38,7 +38,7 @@ def test_format_coloured_string() -> None: message="message", linenumber=1, details=DETAILS, - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=rule, ) formatter.format(match) @@ -50,7 +50,7 @@ def test_unicode_format_string() -> None: message="\U0001f427", linenumber=1, details=DETAILS, - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=rule, ) formatter.format(match) @@ -62,7 +62,7 @@ def test_dict_format_line() -> None: message="xyz", linenumber=1, details={"hello": "world"}, # type: ignore - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=rule, ) formatter.format(match) diff --git a/test/test_formatter_json.py b/test/test_formatter_json.py index bf27609563..9e995aa5f1 100644 --- a/test/test_formatter_json.py +++ b/test/test_formatter_json.py @@ -32,7 +32,7 @@ def setup_class(self) -> None: message="message", linenumber=1, details="hello", - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=self.rule, ) ) @@ -41,7 +41,7 @@ def setup_class(self) -> None: message="message", linenumber=2, details="hello", - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=self.rule, ) ) @@ -103,7 +103,7 @@ def test_validate_codeclimate_schema_with_positions(self) -> None: linenumber=1, column=42, details="hello", - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=self.rule, ) ] diff --git a/test/test_formatter_sarif.py b/test/test_formatter_sarif.py index 9509d6ce2b..86cc45154d 100644 --- a/test/test_formatter_sarif.py +++ b/test/test_formatter_sarif.py @@ -36,7 +36,7 @@ def setup_class(self) -> None: linenumber=1, column=10, details="hello", - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=self.rule, tag="yaml[test]", ) @@ -46,7 +46,7 @@ def setup_class(self) -> None: message="message", linenumber=2, details="hello", - filename=Lintable("filename.yml"), + filename=Lintable("filename.yml", content=""), rule=self.rule, tag="yaml[test]", ) diff --git a/test/test_matcherrror.py b/test/test_matcherrror.py index d3e893e28c..041b7ce0af 100644 --- a/test/test_matcherrror.py +++ b/test/test_matcherrror.py @@ -77,8 +77,8 @@ def test_matcherror_invalid() -> None: (MatchError("z"), MatchError("a")), # filenames takes priority in sorting ( - MatchError("a", filename=Lintable("b")), - MatchError("a", filename=Lintable("a")), + MatchError("a", filename=Lintable("b", content="")), + MatchError("a", filename=Lintable("a", content="")), ), # rule id partial-become > rule id no-changed-when ( diff --git a/test/test_yaml_utils.py b/test/test_yaml_utils.py index 2a276c7ae5..c5b4d20331 100644 --- a/test/test_yaml_utils.py +++ b/test/test_yaml_utils.py @@ -23,8 +23,7 @@ @pytest.fixture(name="empty_lintable") def fixture_empty_lintable() -> Lintable: """Return a Lintable with no contents.""" - lintable = Lintable("__empty_file__.yaml") - lintable._content = "" # pylint: disable=protected-access + lintable = Lintable("__empty_file__.yaml", content="") return lintable From 2bdf965b69e90931e021dceda48e24962ce9a748 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Fri, 20 Jan 2023 14:03:46 +0000 Subject: [PATCH 08/36] Allow dumping of SARIF files (#2921) --- .ansible-lint | 3 +++ .github/workflows/tox.yml | 2 +- src/ansiblelint/app.py | 7 ++++++ src/ansiblelint/cli.py | 2 ++ src/ansiblelint/config.py | 1 + .../schemas/ansible-lint-config.json | 5 ++++ test/test_formatter_sarif.py | 25 +++++++++++++++++++ 7 files changed, 44 insertions(+), 1 deletion(-) diff --git a/.ansible-lint b/.ansible-lint index 38a899a8b5..d6ade300f2 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -3,6 +3,9 @@ profile: null # min, basic, moderate,safety, shared, production +# Allows dumping of results in SARIF format +# sarif_file: result.sarif + # exclude_paths included in this file are parsed relative to this file's location # and not relative to the CWD of execution. CLI arguments passed to the --exclude # option are parsed relative to the CWD of execution. diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 8c03b4925b..a6e89f3da8 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -69,7 +69,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 767 + PYTEST_REQPASS: 769 steps: - name: Activate WSL1 diff --git a/src/ansiblelint/app.py b/src/ansiblelint/app.py index 40423c64d7..1b49611801 100644 --- a/src/ansiblelint/app.py +++ b/src/ansiblelint/app.py @@ -88,6 +88,13 @@ def render_matches(self, matches: list[MatchError]) -> None: for match in itertools.chain(fatal_matches, ignored_matches): console.print(formatter.format(match), markup=False, highlight=False) + # If sarif_file is set, we also dump the results to a sarif file. + if self.options.sarif_file: + sarif = formatters.SarifFormatter(self.options.cwd, True) + json = sarif.format_result(matches) + with open(self.options.sarif_file, "w", encoding="utf-8") as sarif_file: + sarif_file.write(json) + def count_results(self, matches: list[MatchError]) -> SummarizedResults: """Count failures and warnings in matches.""" result = SummarizedResults() diff --git a/src/ansiblelint/cli.py b/src/ansiblelint/cli.py index b111e10b68..8e7d559502 100644 --- a/src/ansiblelint/cli.py +++ b/src/ansiblelint/cli.py @@ -252,6 +252,7 @@ def get_cli_parser() -> argparse.ArgumentParser: ], help="stdout formatting, json being an alias for codeclimate. (default: %(default)s)", ) + parser.add_argument("--sarif-file", default=None, help="SARIF output file") parser.add_argument( "-q", dest="quiet", @@ -462,6 +463,7 @@ def merge_config(file_config: dict[Any, Any], cli_config: Namespace) -> Namespac "loop_var_prefix": None, "project_dir": ".", "profile": None, + "sarif_file": None, } if not file_config: diff --git a/src/ansiblelint/config.py b/src/ansiblelint/config.py index c86275d0ca..528c19ffbc 100644 --- a/src/ansiblelint/config.py +++ b/src/ansiblelint/config.py @@ -136,6 +136,7 @@ rules={}, # Placeholder to set and keep configurations for each rule. profile=None, task_name_prefix="{stem} | ", + sarif_file=None, ) # Used to store detected tag deprecations diff --git a/src/ansiblelint/schemas/ansible-lint-config.json b/src/ansiblelint/schemas/ansible-lint-config.json index 48ff882d77..149bf02318 100644 --- a/src/ansiblelint/schemas/ansible-lint-config.json +++ b/src/ansiblelint/schemas/ansible-lint-config.json @@ -183,6 +183,11 @@ "title": "Rulesdir", "type": "array" }, + "sarif_file": { + "default": null, + "title": "SARIF Output filename", + "type": ["null", "string"] + }, "skip_action_validation": { "default": false, "title": "Skip Action Validation", diff --git a/test/test_formatter_sarif.py b/test/test_formatter_sarif.py index 86cc45154d..7ef87b53c2 100644 --- a/test/test_formatter_sarif.py +++ b/test/test_formatter_sarif.py @@ -2,9 +2,11 @@ from __future__ import annotations import json +import os import pathlib import subprocess import sys +from tempfile import NamedTemporaryFile import pytest @@ -140,3 +142,26 @@ def test_sarif_parsable_ignored() -> None: assert result.returncode == result2.returncode assert result.stdout == result2.stdout + + +@pytest.mark.parametrize( + ("file", "return_code"), + ( + pytest.param("examples/playbooks/valid.yml", 0), + pytest.param("playbook.yml", 2), + ), +) +def test_sarif_file(file: str, return_code: int) -> None: + """Test ability to dump sarif file (--sarif-file).""" + with NamedTemporaryFile(mode="w", suffix=".sarif", prefix="output") as output_file: + cmd = [ + sys.executable, + "-m", + "ansiblelint", + "--sarif-file", + str(output_file.name), + ] + result = subprocess.run([*cmd, file], check=False, capture_output=True) + assert result.returncode == return_code + assert os.path.exists(output_file.name) + assert os.path.getsize(output_file.name) > 0 From c1559bcea65df5a22875c7a31f5a04e7c3dbf01a Mon Sep 17 00:00:00 2001 From: Thom Wiggers Date: Fri, 20 Jan 2023 15:23:38 +0100 Subject: [PATCH 09/36] Fix spacing in option help strings (#2920) --- src/ansiblelint/cli.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ansiblelint/cli.py b/src/ansiblelint/cli.py index 8e7d559502..f6f565ee6c 100644 --- a/src/ansiblelint/cli.py +++ b/src/ansiblelint/cli.py @@ -281,8 +281,8 @@ def get_cli_parser() -> argparse.ArgumentParser: dest="progressive", default=False, action="store_true", - help="Return success if number of violations compared with" - "previous git commit has not increased. This feature works" + help="Return success if number of violations compared with " + "previous git commit has not increased. This feature works " "only in git repositories.", ) parser.add_argument( @@ -290,7 +290,7 @@ def get_cli_parser() -> argparse.ArgumentParser: dest="project_dir", default=".", help="Location of project/repository, autodetected based on location " - " of configuration file.", + "of configuration file.", ) parser.add_argument( "-r", @@ -364,7 +364,7 @@ def get_cli_parser() -> argparse.ArgumentParser: dest="skip_list", default=[], action="append", - help="only check rules whose id/tags do not " "match these values", + help="only check rules whose id/tags do not match these values", ) parser.add_argument( "-w", @@ -426,8 +426,7 @@ def get_cli_parser() -> argparse.ArgumentParser: dest="lintables", nargs="*", action="extend", - help="One or more files or paths. When missing it will " - " enable auto-detection mode.", + help="One or more files or paths. When missing it will enable auto-detection mode.", ) return parser From b7dca8c4535b2ea5d895797d8bae18e661d7726e Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Fri, 20 Jan 2023 14:24:10 +0000 Subject: [PATCH 10/36] Improve installation and support instructions (#2918) * Improve installation instructions and support Fixes: #2909 * Update docs/installing.md Co-authored-by: Don Naro * Update docs/installing.md Co-authored-by: Don Naro * Update docs/installing.md Co-authored-by: Don Naro * Update docs/installing.md Co-authored-by: Don Naro * Update docs/installing.md Co-authored-by: Don Naro * Update docs/installing.md Co-authored-by: Don Naro * Update docs/installing.md Co-authored-by: Don Naro * Update docs/installing.md Co-authored-by: Don Naro Co-authored-by: Don Naro --- docs/installing.md | 50 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/docs/installing.md b/docs/installing.md index 6fbeb0d421..b63731f9e8 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -6,21 +6,49 @@ ``` -Install Ansible-lint to apply rules and follow best practices with your automation content. +Install Ansible-lint to apply rules and follow best practices with your +automation content. ```{note} Ansible-lint does not currently support installation on Windows systems. ``` -For a container image, we recommend using [creator-ee](https://github.com/ansible/creator-ee/), which includes Ansible-lint. -If you have a use case that the `creator-ee` container doesn't satisfy, please contact the team through the [discussions](https://github.com/ansible/ansible-lint/discussions) forum. +```{warning} +Ansible-lint does not support any installation methods that are not +mentioned in this document. Before raising any bugs related to installation, +review all of the following details: + +- You should use installation methods outlined in this document only. +- You should upgrade the Python installer (`pip` or `pipx`) to the latest version + available from pypi.org. If you used a system package manager, you + will need to upgrade the installer to a newer version. +- If you are installing from a git zip archive, which is not supported but should work, + ensure you use the main branch and the latest version of pip and setuptools. +- If you are installing Ansible-lint within a container or system package, you + should not report the issue here. Contact the relevant container or package + provider instead. +- If you are using [poetry](https://python-poetry.org/), read this + [discussion](https://github.com/ansible/ansible-lint/discussions/2820#discussioncomment-4400380). + +Pull requests to improve installation instructions are welcome. Any new issues related to +installation will be closed and locked. +``` + +For a container image, we recommend using +[creator-ee](https://github.com/ansible/creator-ee/), which includes +Ansible-lint. If you have a use case that the `creator-ee` container doesn't +satisfy, please contact the team through the +[discussions](https://github.com/ansible/ansible-lint/discussions) forum. -You can also run Ansible-lint on your source code with the [Ansible-lint GitHub action](https://github.com/marketplace/actions/ansible-lint) instead of installing it directly. +You can also run Ansible-lint on your source code with the +[Ansible-lint GitHub action](https://github.com/marketplace/actions/ansible-lint) +instead of installing it directly. ## Installing the latest version -You can install the most recent version of Ansible-lint with the [pip3] or [pipx] Python package manager. -Use [pipx] to isolate Ansible-lint from your current Python environment as an alternative to creating a virtual environment. +You can install the most recent version of Ansible-lint with the [pip3] or +[pipx] Python package manager. Use [pipx] to isolate Ansible-lint from your +current Python environment as an alternative to creating a virtual environment. ```bash # This also installs ansible-core if it is not already installed @@ -29,7 +57,8 @@ pip3 install "ansible-lint" ## Installing on Fedora and RHEL -You can install Ansible-lint on Fedora, or Red Hat Enterprise Linux (RHEL) with the `dnf` package manager. +You can install Ansible-lint on Fedora, or Red Hat Enterprise Linux (RHEL) with +the `dnf` package manager. ```bash dnf install ansible-lint @@ -42,14 +71,15 @@ to be activated. ## Installing from source code -**Note**: pip 19.0+ is required for installation from the source repository. +**Note**: `pip>=22.3.1` is required for installation from the source repository. Please consult the [PyPA User Guide] to learn more about managing Pip versions. ```bash -pip3 install git+https://github.com/ansible/ansible-lint.git +pip3 install git+https://github.com/ansible/ansible-lint ``` [installing_from_source]: https://pypi.org/project/pip/ [pip3]: https://pypi.org/project/pip/ [pipx]: https://pypa.github.io/pipx/ -[pypa user guide]: https://packaging.python.org/en/latest/tutorials/installing-packages/#ensure-pip-setuptools-and-wheel-are-up-to-date +[pypa user guide]: + https://packaging.python.org/en/latest/tutorials/installing-packages/#ensure-pip-setuptools-and-wheel-are-up-to-date From 6087f67241abfa6c1176f9602d02aeae3eadb834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Fri, 20 Jan 2023 16:30:49 +0100 Subject: [PATCH 11/36] Extend fqcn rule to complain on `collections` keyword (#2825) --- examples/playbooks/rule-fqcn-fail.yml | 4 ++++ src/ansiblelint/rules/fqcn.md | 3 ++- src/ansiblelint/rules/fqcn.py | 27 +++++++++++++++++++++----- src/ansiblelint/schemas/__store__.json | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/examples/playbooks/rule-fqcn-fail.yml b/examples/playbooks/rule-fqcn-fail.yml index c3ff17b44b..289fee3713 100644 --- a/examples/playbooks/rule-fqcn-fail.yml +++ b/examples/playbooks/rule-fqcn-fail.yml @@ -1,10 +1,14 @@ --- - name: Fixture hosts: localhost + collections: + - community.general tasks: - name: Shell (fqcn[action-core]) # noqa: command-instead-of-shell shell: echo This rule should get matched by the fqcn rule + changed_when: false - name: Shell (fqcn[action]) ini_file: + section: foo path: /tmp/test.ini mode: 0644 diff --git a/src/ansiblelint/rules/fqcn.md b/src/ansiblelint/rules/fqcn.md index 18fb917b1a..68358b35d3 100644 --- a/src/ansiblelint/rules/fqcn.md +++ b/src/ansiblelint/rules/fqcn.md @@ -10,6 +10,7 @@ The `fqcn` rule has the following checks: - `fqcn[action]` - Use FQCN for module actions, such ... - `fqcn[action-core]` - Checks for FQCNs from the `ansible.legacy` or `ansible.builtin` collection. - `fqcn[canonical]` - You should use canonical module name ... instead of ... +- `fqcn[keyword]` - Avoid `collections` keyword by using FQCN for all plugins, modules, roles and playbooks. ```{note} In most cases you should declare the `ansible.builtin` collection for internal Ansible actions. @@ -17,7 +18,7 @@ You should declare the `ansible.legacy` collection if you use local overrides wi ``` ```{warning} -This rule does not take [`collections` keyword](https://docs.ansible.com/ansible/latest/collections_guide/collections_using_playbooks.html#simplifying-module-names-with-the-collections-keyword) into consideration. +This rule does not take [`collections` keyword](https://docs.ansible.com/ansible/latest/collections_guide/collections_using_playbooks.html#simplifying-module-names-with-the-collections-keyword) into consideration for resolving content. The `collections` keyword provided a temporary mechanism transitioning to Ansible 2.9. You should rewrite any content that uses the `collections:` key and avoid it where possible. ``` diff --git a/src/ansiblelint/rules/fqcn.py b/src/ansiblelint/rules/fqcn.py index 106fd0c8a1..3054cd7511 100644 --- a/src/ansiblelint/rules/fqcn.py +++ b/src/ansiblelint/rules/fqcn.py @@ -7,6 +7,7 @@ from ansible.plugins.loader import module_loader +from ansiblelint.constants import LINE_NUMBER_KEY from ansiblelint.errors import MatchError from ansiblelint.file_utils import Lintable from ansiblelint.rules import AnsibleLintRule, RulesCollection @@ -159,6 +160,20 @@ def matchtask( ) return result + def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]: + if file.kind != "playbook": + return [] + if "collections" in data: + return [ + self.create_matcherror( + message="Avoid `collections` keyword by using FQCN for all plugins, modules, roles and playbooks.", + linenumber=data[LINE_NUMBER_KEY], + tag="fqcn[keyword]", + filename=file, + ) + ] + return [] + # testing code to be loaded only with pytest or when executed the rule file if "pytest" in sys.modules: @@ -171,11 +186,13 @@ def test_fqcn_builtin_fail() -> None: collection.register(FQCNBuiltinsRule()) success = "examples/playbooks/rule-fqcn-fail.yml" results = Runner(success, rules=collection).run() - assert len(results) == 2 - assert results[0].tag == "fqcn[action-core]" - assert "Use FQCN for builtin module actions" in results[0].message - assert results[1].tag == "fqcn[action]" - assert "Use FQCN for module actions, such" in results[1].message + assert len(results) == 3 + assert results[0].tag == "fqcn[keyword]" + assert "Avoid `collections` keyword" in results[0].message + assert results[1].tag == "fqcn[action-core]" + assert "Use FQCN for builtin module actions" in results[1].message + assert results[2].tag == "fqcn[action]" + assert "Use FQCN for module actions, such" in results[2].message def test_fqcn_builtin_pass() -> None: """Test rule does not match.""" diff --git a/src/ansiblelint/schemas/__store__.json b/src/ansiblelint/schemas/__store__.json index 4b29a1269f..dea306ef94 100644 --- a/src/ansiblelint/schemas/__store__.json +++ b/src/ansiblelint/schemas/__store__.json @@ -1,6 +1,6 @@ { "ansible-lint-config": { - "etag": "9ee4e29fc03cdf2edf8c7a93667cab776d7cc5d47ce1c9950ac315e0af0249e2", + "etag": "06ab98c24dd449836b25c359ab655bc9bd4cab961535ba705540ae4b35248ac7", "url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible-lint-config.json" }, "ansible-navigator-config": { From 04204dc457d758bd3bf7ec8058b7b9788eb58f34 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Fri, 20 Jan 2023 16:56:09 +0000 Subject: [PATCH 12/36] Allow warnings to be skipped (#2925) Fixes: #2868 Closes: #2917 --- .github/workflows/tox.yml | 2 +- src/ansiblelint/__main__.py | 3 +++ test/test_with_skip_tagid.py | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index a6e89f3da8..7864c83b93 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -69,7 +69,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 769 + PYTEST_REQPASS: 770 steps: - name: Activate WSL1 diff --git a/src/ansiblelint/__main__.py b/src/ansiblelint/__main__.py index fd618a2fac..e706bdda59 100755 --- a/src/ansiblelint/__main__.py +++ b/src/ansiblelint/__main__.py @@ -281,6 +281,9 @@ def main(argv: list[str] | None = None) -> int: # noqa: C901 if options.strict and result.matches: mark_as_success = False + # Remove skipped list items from the result + result.matches = [m for m in result.matches if m.tag not in app.options.skip_list] + app.render_matches(result.matches) _perform_mockings_cleanup() diff --git a/test/test_with_skip_tagid.py b/test/test_with_skip_tagid.py index 1390bf1287..5f710ba8f3 100644 --- a/test/test_with_skip_tagid.py +++ b/test/test_with_skip_tagid.py @@ -2,6 +2,7 @@ from ansiblelint.rules import RulesCollection from ansiblelint.rules.yaml_rule import YamllintRule from ansiblelint.runner import Runner +from ansiblelint.testing import run_ansible_lint FILE = "examples/playbooks/with-skip-tag-id.yml" collection = RulesCollection() @@ -43,3 +44,15 @@ def test_positive_skip_tag() -> None: skip_tag = "yaml[trailing-spaces]" good_runner = Runner(FILE, rules=collection, skip_list=[skip_tag]) assert [] == good_runner.run() + + +def test_run_skip_warning_rule() -> None: + """Test that we can skip warning[empty-playbook].""" + result = run_ansible_lint( + "-x", + "warning[empty-playbook]", + "examples/playbooks/empty_playbook.yml", + executable="ansible-lint", + ) + assert result.returncode == 0 + assert result.stdout == "" From 18a42e10d985cf9f8817d2452dc48ed12a8e1beb Mon Sep 17 00:00:00 2001 From: Klaus Sperner <78594269+klaus-tux@users.noreply.github.com> Date: Tue, 24 Jan 2023 11:14:34 +0100 Subject: [PATCH 13/36] Ignore args rule finding if value for choice has jinja template (#2929) * Ignore args rule finding if value for choice has jinja template Fixes: #2866 * Add correctly failing example for value not in choice * make cspell happy --- .../playbooks/rule-args-module-fail-1.yml | 6 ++++++ .../playbooks/rule-args-module-pass-1.yml | 9 +++++++++ src/ansiblelint/rules/args.py | 20 ++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/examples/playbooks/rule-args-module-fail-1.yml b/examples/playbooks/rule-args-module-fail-1.yml index c4b68da00a..4c9b51e335 100644 --- a/examples/playbooks/rule-args-module-fail-1.yml +++ b/examples/playbooks/rule-args-module-fail-1.yml @@ -28,3 +28,9 @@ foo: # this is a nested object which will have the __ injections # that we later need to clean bar: true + + - name: Remove deployment dir + # module should produce: 'value of state must be one of: absent, directory, file, hard, link, touch, got: away' + ansible.builtin.file: + path: /opt/software/deployment + state: away diff --git a/examples/playbooks/rule-args-module-pass-1.yml b/examples/playbooks/rule-args-module-pass-1.yml index 5056059038..9afbebd9a3 100644 --- a/examples/playbooks/rule-args-module-pass-1.yml +++ b/examples/playbooks/rule-args-module-pass-1.yml @@ -15,3 +15,12 @@ name: httpd enabled: false masked: false + + - name: Clear deployment dir + ansible.builtin.file: + path: /opt/software/deployment + state: "{{ item }}" + mode: 0755 + with_items: + - absent + - directory diff --git a/src/ansiblelint/rules/args.py b/src/ansiblelint/rules/args.py index 87c0aa4acd..90705be4d7 100644 --- a/src/ansiblelint/rules/args.py +++ b/src/ansiblelint/rules/args.py @@ -214,6 +214,22 @@ def _parse_failed_msg( ) return results + value_not_in_choices_error = re.search( + r"value of (?P.*) must be one of:", error_message + ) + if value_not_in_choices_error: + # ignore templated value not in allowed choices + choice_key = value_not_in_choices_error.group("name") + choice_value = task["action"][choice_key] + if has_jinja(choice_value): + _logger.debug( + "Value checking ignored for '%s' option in task '%s' at line %s.", + choice_key, + module_name, + task[LINE_NUMBER_KEY], + ) + return results + results.append( self.create_matcherror( message=error_message, @@ -236,7 +252,7 @@ def test_args_module_fail() -> None: collection.register(ArgsRule()) success = "examples/playbooks/rule-args-module-fail-1.yml" results = Runner(success, rules=collection).run() - assert len(results) == 4 + assert len(results) == 5 assert results[0].tag == "args[module]" assert "missing required arguments" in results[0].message assert results[1].tag == "args[module]" @@ -245,6 +261,8 @@ def test_args_module_fail() -> None: assert "Unsupported parameters for" in results[2].message assert results[3].tag == "args[module]" assert "Unsupported parameters for" in results[2].message + assert results[4].tag == "args[module]" + assert "value of state must be one of" in results[4].message def test_args_module_pass() -> None: """Test rule valid module options.""" From 062cb4e6b98294f3361545f6784eb368c07dfe67 Mon Sep 17 00:00:00 2001 From: Ajinkya Udgirkar Date: Wed, 25 Jan 2023 15:17:02 +0530 Subject: [PATCH 14/36] Improving test coverage (#2933) * WIP: Improving test coverage * WIP: Improving test coverage Co-authored-by: Ajinkya Udgirkar --- .github/workflows/tox.yml | 2 +- .../rules/command_instead_of_module.py | 15 +++++++++++++++ src/ansiblelint/rules/command_instead_of_shell.py | 9 ++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 7864c83b93..b45889374e 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -69,7 +69,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 770 + PYTEST_REQPASS: 771 steps: - name: Activate WSL1 diff --git a/src/ansiblelint/rules/command_instead_of_module.py b/src/ansiblelint/rules/command_instead_of_module.py index e50b829a92..98de6ab841 100644 --- a/src/ansiblelint/rules/command_instead_of_module.py +++ b/src/ansiblelint/rules/command_instead_of_module.py @@ -168,6 +168,13 @@ def matchtask( command: yum clean all """ + NO_COMMAND = """ +- hosts: all + tasks: + - name: Clear yum cache + command: "" +""" + @pytest.mark.parametrize( "rule_runner", (CommandsInsteadOfModulesRule,), indirect=["rule_runner"] ) @@ -231,3 +238,11 @@ def test_yum_clean(rule_runner: RunFromText) -> None: """The yum module does not support clearing yum cache.""" results = rule_runner.run_playbook(YUM_CLEAN) assert len(results) == 0 + + @pytest.mark.parametrize( + "rule_runner", (CommandsInsteadOfModulesRule,), indirect=["rule_runner"] + ) + def test_no_command(rule_runner: RunFromText) -> None: + """If no command is passed it should return 0.""" + results = rule_runner.run_playbook(NO_COMMAND) + assert len(results) == 0 diff --git a/src/ansiblelint/rules/command_instead_of_shell.py b/src/ansiblelint/rules/command_instead_of_shell.py index 9a2c3a2392..626aac3ae8 100644 --- a/src/ansiblelint/rules/command_instead_of_shell.py +++ b/src/ansiblelint/rules/command_instead_of_shell.py @@ -43,10 +43,17 @@ cmd: echo {{ "hello" | upper }} changed_when: false - - name: Sshell with jinja filter (fqcn) + - name: Shell with jinja filter (fqcn) ansible.builtin.shell: cmd: echo {{ "hello" | upper }} changed_when: false + + - name: Command with executable parameter + ansible.builtin.shell: + cmd: clear + args: + executable: /bin/bash + changed_when: false """ SUCCESS_PLAY = """--- From a9607e22cd52ad567295aa7d78004f9fec1c7c7e Mon Sep 17 00:00:00 2001 From: Steffen Scheib <37306894+sscheib@users.noreply.github.com> Date: Wed, 25 Jan 2023 13:16:02 +0100 Subject: [PATCH 15/36] Fixing OpenWrt name; Adding OpenWrt 22.03 (#2928) * Fixing OpenWrt name (was OpenWRT, which is wrong); Adding latest OpenWrt version 22.03 * Removing bad char at version definition --- src/ansiblelint/schemas/meta.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ansiblelint/schemas/meta.json b/src/ansiblelint/schemas/meta.json index 88cd124d2c..41cf21a605 100644 --- a/src/ansiblelint/schemas/meta.json +++ b/src/ansiblelint/schemas/meta.json @@ -754,23 +754,23 @@ "title": "OpenBSDPlatformModel", "type": "object" }, - "OpenWRTPlatformModel": { + "OpenWrtPlatformModel": { "properties": { "name": { - "const": "OpenWRT", + "const": "OpenWrt", "title": "Name", "type": "string" }, "versions": { "default": "all", "items": { - "enum": ["17.01", "18.06", "19.07", "21.02", "all"], + "enum": ["17.01", "18.06", "19.07", "21.02", "22.03", "all"], "type": "string" }, "type": "array" } }, - "title": "OpenWRTPlatformModel", + "title": "OpenWrtPlatformModel", "type": "object" }, "OracleLinuxPlatformModel": { @@ -1268,7 +1268,7 @@ "$ref": "#/$defs/opensusePlatformModel" }, { - "$ref": "#/$defs/OpenWRTPlatformModel" + "$ref": "#/$defs/OpenWrtPlatformModel" }, { "$ref": "#/$defs/OracleLinuxPlatformModel" From aa0daccb8f463994b5836f74816dbdeb5398cee0 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 25 Jan 2023 14:35:05 +0000 Subject: [PATCH 16/36] Disable branch coverage due to upstream bug (#2937) Related: https://github.com/nedbat/coveragepy/issues/605 --- pyproject.toml | 6 ++++-- tox.ini | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 68cc465b12..a70bf1f009 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,9 @@ ignore-words-list = "indention" [tool.coverage.run] source = ["src"] -branch = true +# Do not use branch until bug is fixes: +# https://github.com/nedbat/coveragepy/issues/605 +# branch = true parallel = true concurrency = ["multiprocessing", "thread"] @@ -203,7 +205,7 @@ python_files = [ xfail_strict = true markers = [ "eco: Tests effects on a set of 3rd party ansible repositories", - "formatting_fixtures: Test that regenerates and tests formatting fixtures (requires prettier on PATH)" + "formatting_fixtures: Test that regenerates and tests formatting fixtures (requires prettier on PATH)", ] [tool.setuptools.dynamic] diff --git a/tox.ini b/tox.ini index c87519476a..bf06627e24 100644 --- a/tox.ini +++ b/tox.ini @@ -184,7 +184,7 @@ skip_install = true deps = {[testenv]deps} commands = - python -m venv .tox/venv + python3 -m venv .tox/venv .tox/venv/bin/pip install -q git+https://github.com/ansible/ansible-lint@main pytest -n auto --durations=3 -m eco allowlist_externals = @@ -229,10 +229,10 @@ usedevelop = false setenv = COVERAGE_PROCESS_START={toxinidir}/pyproject.toml commands = - python -m coverage --version + python3 -m coverage --version # needed by codecov github actions, also ignored result to reach report one. - python -m coverage xml --fail-under=0 + python3 -m coverage xml --fail-under=0 # just for humans running it: - coverage report + python3 -m coverage report deps = coverage[toml]>=7.0.5 From f42dbdbf3fe9a05799d5a8b39997260876cebb06 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 25 Jan 2023 15:27:51 +0000 Subject: [PATCH 17/36] Update schema cache (#2939) --- src/ansiblelint/schemas/__store__.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ansiblelint/schemas/__store__.json b/src/ansiblelint/schemas/__store__.json index dea306ef94..5d69b30396 100644 --- a/src/ansiblelint/schemas/__store__.json +++ b/src/ansiblelint/schemas/__store__.json @@ -4,7 +4,7 @@ "url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible-lint-config.json" }, "ansible-navigator-config": { - "etag": "259a168c473dc88b5def7ea8c7ed28365e46a8abf894cd08fcd57613508e1f26", + "etag": "a330d162e7e4b168633c03ba2898c9b499f0e97eccbded6a1467bb94aa4d2ccc", "url": "https://raw.githubusercontent.com/ansible/ansible-navigator/main/src/ansible_navigator/data/ansible-navigator.json" }, "arg_specs": { @@ -28,7 +28,7 @@ "url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/inventory.json" }, "meta": { - "etag": "1f128994877dd4f9d0712ed36930d6f14d7cd9d601b459d9f19fa7104e626f51", + "etag": "090cc0a998e0251c48ecf91b62607fb93cc75cf8553545a4fa26fa808873d236", "url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/meta.json" }, "meta-runtime": { From c3ef3f3ec5fc2ed54390cb2f2a0782c7b571d969 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 25 Jan 2023 15:37:25 +0000 Subject: [PATCH 18/36] Remove rich markdown header override (#2938) As rich 13.2 is changing internals related to markdown rendering, it is better to remove our custom header rendering override, so users can use the library with any version of rich. --- .config/requirements-lock.txt | 2 +- .config/requirements.txt | 3 +-- .pre-commit-config.yaml | 10 +++++----- src/ansiblelint/color.py | 13 ------------- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/.config/requirements-lock.txt b/.config/requirements-lock.txt index 6c9b3666d4..6e111e0f0c 100644 --- a/.config/requirements-lock.txt +++ b/.config/requirements-lock.txt @@ -26,7 +26,7 @@ pygments==2.13.0 pyrsistent==0.19.3 pyyaml==6.0 resolvelib==0.8.1 -rich==13.0.0 +rich==13.2.0 ruamel-yaml==0.17.21 setuptools==65.6.3 subprocess-tee==0.4.1 diff --git a/.config/requirements.txt b/.config/requirements.txt index d601031309..c273a5d80e 100644 --- a/.config/requirements.txt +++ b/.config/requirements.txt @@ -16,7 +16,6 @@ certifi==2022.12.7 cffi==1.15.1 charset-normalizer==3.0.1 click==8.1.3 -commonmark==0.9.1 coverage==7.0.5 coverage-enable-subprocess==1.0 cryptography==39.0.0 @@ -64,7 +63,7 @@ pytz==2022.7.1 pyyaml==6.0 requests==2.28.2 resolvelib==0.8.1 -rich==13.1.0 +rich==13.2.0 ruamel-yaml==0.17.21 ruamel-yaml-clib==0.2.7 setuptools==66.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 35ee91f36b..0bdd002602 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,14 +56,14 @@ repos: - prettier-plugin-toml - prettier-plugin-sort-json - repo: https://github.com/streetsidesoftware/cspell-cli - rev: v6.17.1 + rev: v6.19.2 hooks: - id: cspell # entry: codespell --relative args: [--relative, --no-progress, --no-summary] name: Spell check with cspell - repo: https://github.com/sirosen/check-jsonschema - rev: 0.20.0 + rev: 0.21.0 hooks: - id: check-github-workflows - repo: https://github.com/pre-commit/pre-commit-hooks.git @@ -169,7 +169,7 @@ repos: - filelock - jinja2 - pytest>=7.2.0 - - rich>=11.0.0 + - rich>=13.2.0 - ruamel.yaml - types-PyYAML - types-jsonschema>=4.4.2 @@ -182,7 +182,7 @@ repos: plugins/.* )$ - repo: https://github.com/pycqa/pylint - rev: v2.16.0b0 + rev: v2.16.0b1 hooks: - id: pylint args: @@ -196,7 +196,7 @@ repos: - jsonschema>=4.9.0 - pytest>=7.2.0 - pyyaml - - rich>=11.0.0 + - rich>=13.2.0 - ruamel.yaml - sphinx - typing_extensions diff --git a/src/ansiblelint/color.py b/src/ansiblelint/color.py index 917bf195c8..373ef78fb4 100644 --- a/src/ansiblelint/color.py +++ b/src/ansiblelint/color.py @@ -88,16 +88,6 @@ def render_yaml(text: str) -> Syntax: return Syntax(text, "yaml", theme="ansi_dark") -# pylint: disable=redefined-outer-name,unused-argument -def _rich_heading_custom_rich_console( - self: rich.markdown.Heading, - console: rich.console.Console, - options: rich.console.ConsoleOptions, -) -> rich.console.RenderResult: - """Override for rich console heading.""" - yield f"[bold]{self.level * '#'} {self.text}[/]" - - # pylint: disable=redefined-outer-name,unused-argument def _rich_codeblock_custom_rich_console( self: rich.markdown.CodeBlock, @@ -115,7 +105,4 @@ def _rich_codeblock_custom_rich_console( yield syntax -# Monkey-patch rich to alter its rendering of headings -# https://github.com/python/mypy/issues/2427 -rich.markdown.Heading.__rich_console__ = _rich_heading_custom_rich_console # type: ignore rich.markdown.CodeBlock.__rich_console__ = _rich_codeblock_custom_rich_console # type: ignore From c89d58644dedc51cd92ef2032795f2ded3c9c875 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Jan 2023 14:35:35 +0000 Subject: [PATCH 19/36] Bump Vampire/setup-wsl from 1 to 2 (#2944) Bumps [Vampire/setup-wsl](https://github.com/Vampire/setup-wsl) from 1 to 2. - [Release notes](https://github.com/Vampire/setup-wsl/releases) - [Commits](https://github.com/Vampire/setup-wsl/compare/v1...v2) --- updated-dependencies: - dependency-name: Vampire/setup-wsl dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/tox.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index b45889374e..fe334a5a1c 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -74,7 +74,7 @@ jobs: steps: - name: Activate WSL1 if: "contains(matrix.shell, 'wsl')" - uses: Vampire/setup-wsl@v1 + uses: Vampire/setup-wsl@v2 - name: MacOS workaround for https://github.com/actions/virtual-environments/issues/1187 if: ${{ matrix.os == 'macOS-latest' }} From fd90545c4a2527e6ff4c76f0c1f69893da6cb200 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Thu, 26 Jan 2023 17:22:32 +0000 Subject: [PATCH 20/36] Replace sphinx with mkdocs (#2942) Related: https://github.com/ansible/devtools/issues/100 --- .config/dictionary.txt | 12 +- .config/requirements-docs.txt | 19 +- .config/requirements.txt | 47 +-- .github/CONTRIBUTING.md | 63 ---- .gitignore | 3 +- .pre-commit-config.yaml | 1 - .readthedocs.yml | 59 +-- a.ansi | 1 + docs/README.md | 9 - docs/conf.py | 338 ------------------ docs/configuring.md | 40 ++- docs/contributing.md | 110 +++++- docs/custom-rules.md | 43 ++- docs/images/favicon.ico | Bin 0 -> 15406 bytes docs/images/logo.png | Bin 0 -> 10993 bytes docs/images/logo.svg | 7 + docs/index.md | 62 +--- docs/installing.md | 63 ++-- docs/profiles.md | 113 ++++++ docs/rules.md | 10 - docs/rules/.gitignore | 2 - docs/rules/args.md | 1 + docs/rules/avoid-implicit.md | 1 + docs/rules/command-instead-of-module.md | 1 + docs/rules/command-instead-of-shell.md | 1 + docs/rules/deprecated-bare-vars.md | 1 + docs/rules/deprecated-command-syntax.md | 1 + docs/rules/deprecated-local-action.md | 1 + docs/rules/deprecated-module.md | 1 + docs/rules/empty-string-compare.md | 1 + docs/rules/fqcn.md | 1 + docs/rules/galaxy.md | 1 + docs/rules/ignore-errors.md | 1 + docs/rules/index.md | 52 +++ docs/rules/inline-env-var.md | 1 + docs/rules/internal-error.md | 1 + docs/rules/jinja.md | 1 + docs/rules/key-order.md | 1 + docs/rules/latest.md | 1 + docs/rules/literal-compare.md | 1 + docs/rules/load-failure.md | 1 + docs/rules/loop-var-prefix.md | 1 + docs/rules/meta-incorrect.md | 1 + docs/rules/meta-no-info.md | 1 + docs/rules/meta-no-tags.md | 1 + docs/rules/meta-unsupported-ansible.md | 1 + docs/rules/meta-video-links.md | 1 + docs/rules/name.md | 1 + docs/rules/no-changed-when.md | 1 + docs/rules/no-free-form.md | 1 + docs/rules/no-handler.md | 1 + docs/rules/no-jinja-when.md | 1 + docs/rules/no-log-password.md | 1 + docs/rules/no-prompting.md | 1 + docs/rules/no-relative-paths.md | 1 + docs/rules/no-same-owner.md | 1 + docs/rules/no-tabs.md | 1 + docs/rules/only-builtins.md | 1 + docs/rules/package-latest.md | 1 + docs/rules/parser-error.md | 1 + docs/rules/partial-become.md | 1 + docs/rules/playbook-extension.md | 1 + docs/rules/risky-file-permissions.md | 1 + docs/rules/risky-octal.md | 1 + docs/rules/risky-shell-pipe.md | 1 + docs/rules/role-name.md | 1 + docs/rules/run-once.md | 1 + docs/rules/schema.md | 1 + docs/rules/syntax-check.md | 1 + docs/rules/var-naming.md | 1 + docs/rules/warning.md | 1 + docs/rules/yaml.md | 1 + docs/stylesheets/extra.css | 174 +++++++++ docs/usage.md | 216 +++++++---- docs/using-profiles.md | 44 --- mkdocs.yml | 195 ++++++++++ pyproject.toml | 2 +- src/ansiblelint/_internal/load-failure.md | 12 + src/ansiblelint/_internal/parser-error.md | 5 + src/ansiblelint/generate_docs.py | 7 +- src/ansiblelint/rules/fqcn.md | 44 +-- src/ansiblelint/rules/name.md | 36 +- src/ansiblelint/rules/no_free_form.md | 19 +- src/ansiblelint/rules/no_relative_paths.md | 36 +- src/ansiblelint/rules/no_tabs.md | 12 +- src/ansiblelint/rules/partial_become.md | 19 +- .../rules/risky_file_permissions.md | 24 +- src/ansiblelint/rules/run_once.md | 14 +- tox.ini | 43 +-- 89 files changed, 1120 insertions(+), 886 deletions(-) delete mode 100644 .github/CONTRIBUTING.md create mode 100644 a.ansi delete mode 100644 docs/README.md delete mode 100644 docs/conf.py create mode 100644 docs/images/favicon.ico create mode 100644 docs/images/logo.png create mode 100644 docs/images/logo.svg create mode 100644 docs/profiles.md delete mode 100644 docs/rules.md delete mode 100644 docs/rules/.gitignore create mode 120000 docs/rules/args.md create mode 120000 docs/rules/avoid-implicit.md create mode 120000 docs/rules/command-instead-of-module.md create mode 120000 docs/rules/command-instead-of-shell.md create mode 120000 docs/rules/deprecated-bare-vars.md create mode 120000 docs/rules/deprecated-command-syntax.md create mode 120000 docs/rules/deprecated-local-action.md create mode 120000 docs/rules/deprecated-module.md create mode 120000 docs/rules/empty-string-compare.md create mode 120000 docs/rules/fqcn.md create mode 120000 docs/rules/galaxy.md create mode 120000 docs/rules/ignore-errors.md create mode 100644 docs/rules/index.md create mode 120000 docs/rules/inline-env-var.md create mode 120000 docs/rules/internal-error.md create mode 120000 docs/rules/jinja.md create mode 120000 docs/rules/key-order.md create mode 120000 docs/rules/latest.md create mode 120000 docs/rules/literal-compare.md create mode 120000 docs/rules/load-failure.md create mode 120000 docs/rules/loop-var-prefix.md create mode 120000 docs/rules/meta-incorrect.md create mode 120000 docs/rules/meta-no-info.md create mode 120000 docs/rules/meta-no-tags.md create mode 120000 docs/rules/meta-unsupported-ansible.md create mode 120000 docs/rules/meta-video-links.md create mode 120000 docs/rules/name.md create mode 120000 docs/rules/no-changed-when.md create mode 120000 docs/rules/no-free-form.md create mode 120000 docs/rules/no-handler.md create mode 120000 docs/rules/no-jinja-when.md create mode 120000 docs/rules/no-log-password.md create mode 120000 docs/rules/no-prompting.md create mode 120000 docs/rules/no-relative-paths.md create mode 120000 docs/rules/no-same-owner.md create mode 120000 docs/rules/no-tabs.md create mode 120000 docs/rules/only-builtins.md create mode 120000 docs/rules/package-latest.md create mode 120000 docs/rules/parser-error.md create mode 120000 docs/rules/partial-become.md create mode 120000 docs/rules/playbook-extension.md create mode 120000 docs/rules/risky-file-permissions.md create mode 120000 docs/rules/risky-octal.md create mode 120000 docs/rules/risky-shell-pipe.md create mode 120000 docs/rules/role-name.md create mode 120000 docs/rules/run-once.md create mode 120000 docs/rules/schema.md create mode 120000 docs/rules/syntax-check.md create mode 120000 docs/rules/var-naming.md create mode 120000 docs/rules/warning.md create mode 120000 docs/rules/yaml.md create mode 100644 docs/stylesheets/extra.css delete mode 100644 docs/using-profiles.md create mode 100644 mkdocs.yml create mode 100644 src/ansiblelint/_internal/load-failure.md create mode 100644 src/ansiblelint/_internal/parser-error.md diff --git a/.config/dictionary.txt b/.config/dictionary.txt index 1b633b2bca..d32df04fa9 100644 --- a/.config/dictionary.txt +++ b/.config/dictionary.txt @@ -47,6 +47,7 @@ autodetected autodiscovery autodoc autofix +autorefs autoupdate awcrosby backports @@ -118,6 +119,7 @@ ematchtestfile envrc execnet extlinks +facelessuser facter fakerole fileglob @@ -126,6 +128,7 @@ filesspot filetree fips firewalld +fontawesome formatstr formetting formsyntax @@ -149,8 +152,8 @@ hwcksum idempotency importlib iniconfig +inlinehilite insertafter -intersphinx ipwrap isclass iscsi @@ -173,12 +176,14 @@ libbzip libera libyaml lineinfile +linenums linkcheck lintable lintables literalinclude localectl machinectl +magiclink markdownlint matchdir matcherror @@ -191,6 +196,7 @@ maxdepth minversion mkdir mkdocs +mkdocstrings mkdtemp mockings mockreturn @@ -247,6 +253,8 @@ pyenv pygments pylint pylintrc +pymdown +pymdownx pypa pyparsing pypi @@ -296,7 +304,6 @@ skiputils slaveinput sortfunc sourcegraph -sphinxcontrib srpm ssbarnea stylesheet @@ -307,6 +314,7 @@ subschema subschemas substrs subtest +superfences supervisorctl synchronize sysvinit diff --git a/.config/requirements-docs.txt b/.config/requirements-docs.txt index 03e0b588a7..60cbdaf6a9 100644 --- a/.config/requirements-docs.txt +++ b/.config/requirements-docs.txt @@ -1,8 +1,11 @@ -myst-parser >= 0.16.1 -pipdeptree >= 2.2.1 -sphinx >= 4.4.0 -sphinx-ansible-theme >= 0.9.1 -sphinx-rtd-theme >= 1.0.0, < 2.0.0 # 1.0.0 broke rendering -sphinxcontrib-apidoc >= 0.3.0 -sphinxcontrib-programoutput2 >= 2.0a1 - +cairosvg +markdown-exec>=1.0.0 +mkdocs-gen-files>=0.4.0 +mkdocs-material-extensions>=1.1.1 +mkdocs-material>=9.0.6 +mkdocs>=1.4.2 +mkdocstrings-python>=0.8.3 +mkdocstrings>=0.20.0 +pillow +pipdeptree>=2.3.3 +pymdown-extensions>=9.9.2 diff --git a/.config/requirements.txt b/.config/requirements.txt index c273a5d80e..48237b167e 100644 --- a/.config/requirements.txt +++ b/.config/requirements.txt @@ -4,47 +4,57 @@ # # pip-compile --extra=docs --extra=test --no-annotate --output-file=.config/requirements.txt --resolver=backtracking --strip-extras --unsafe-package=ansible-core pyproject.toml # -alabaster==0.7.13 ansible-compat==2.2.7 -ansible-pygments==0.1.1 astroid==2.13.2 attrs==22.2.0 -babel==2.11.0 black==22.12.0 bracex==2.3.post1 +cairocffi==1.4.0 +cairosvg==2.6.0 certifi==2022.12.7 cffi==1.15.1 charset-normalizer==3.0.1 click==8.1.3 +colorama==0.4.6 coverage==7.0.5 coverage-enable-subprocess==1.0 cryptography==39.0.0 +cssselect2==0.7.0 +defusedxml==0.7.1 dill==0.3.6 -docutils==0.17.1 exceptiongroup==1.1.0 execnet==1.9.0 filelock==3.9.0 flake8==6.0.0 flake8-future-annotations==1.0.0 +ghp-import==2.1.0 +griffe==0.25.4 idna==3.4 -imagesize==1.4.1 importlib-metadata==6.0.0 iniconfig==2.0.0 isort==5.11.4 jinja2==3.1.2 jsonschema==4.17.3 lazy-object-proxy==1.9.0 +markdown==3.3.7 +markdown-exec==1.0.0 markdown-it-py==2.1.0 markupsafe==2.1.1 mccabe==0.7.0 -mdit-py-plugins==0.3.3 mdurl==0.1.2 +mergedeep==1.3.4 +mkdocs==1.4.2 +mkdocs-autorefs==0.4.1 +mkdocs-gen-files==0.4.0 +mkdocs-material==9.0.6 +mkdocs-material-extensions==1.1.1 +mkdocstrings==0.20.0 +mkdocstrings-python==0.8.3 mypy==0.991 mypy-extensions==0.4.3 -myst-parser==0.18.1 packaging==23.0 pathspec==0.10.3 -pbr==5.11.1 +pillow==9.4.0 pipdeptree==2.3.3 platformdirs==2.6.2 pluggy==1.0.0 @@ -54,37 +64,32 @@ pycparser==2.21 pyflakes==3.0.1 pygments==2.14.0 pylint==2.15.10 +pymdown-extensions==9.9.2 pyrsistent==0.19.3 pytest==7.2.1 pytest-mock==3.10.0 pytest-plus==0.4.0 pytest-xdist==3.1.0 -pytz==2022.7.1 +python-dateutil==2.8.2 pyyaml==6.0 +pyyaml-env-tag==0.1 +regex==2022.10.31 requests==2.28.2 resolvelib==0.8.1 rich==13.2.0 ruamel-yaml==0.17.21 ruamel-yaml-clib==0.2.7 setuptools==66.0.0 -snowballstemmer==2.2.0 -sphinx==5.3.0 -sphinx-ansible-theme==0.10.1 -sphinx-rtd-theme==1.1.1 -sphinxcontrib-apidoc==0.3.0 -sphinxcontrib-applehelp==1.0.3 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==2.0.0 -sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-programoutput2==2.0a1 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.5 +six==1.16.0 subprocess-tee==0.4.1 +tinycss2==1.2.1 tomli==2.0.1 tomlkit==0.11.6 typing-extensions==4.4.0 urllib3==1.26.14 +watchdog==2.2.1 wcmatch==8.4.1 +webencodings==0.5.1 wrapt==1.14.1 yamllint==1.29.0 zipp==3.11.0 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index f9781070b5..0000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,63 +0,0 @@ -# Contributing to Ansible-lint - -To contribute to ansible-lint, please use pull requests on a branch -of your own fork. - -After [creating your fork on GitHub], you can do: - -```shell-session -$ git clone git@github.com:your-name/ansible-lint -$ cd ansible-lint -$ git checkout -b your-branch-name -# DO SOME CODING HERE -$ git add your new files -$ git commit -v -$ git push origin your-branch-name -``` - -You will then be able to create a pull request from your commit. - -All fixes to core functionality (i.e. anything except docs or examples) -should be accompanied by tests that fail prior to your change and -succeed afterwards. - -Feel free to raise issues in the repo if you feel unable to -contribute a code fix. - -## Standards - -ansible-lint is flake8 compliant with **max-line-length** set to 100. - -ansible-lint works only with supported Ansible versions at the time it was released. - -Automated tests will be run against all PRs for flake8 compliance -and Ansible compatibility — to check before pushing commits, just -use [tox](https://tox.wiki/en/latest/). - -% DO-NOT-REMOVE-deps-snippet-PLACEHOLDER - -## Talk to us - -Use Github [discussions] forum or for a live chat experience try -`#ansible-devtools` IRC channel on libera.chat or Matrix room -[#devtools:ansible.com](https://matrix.to/#/#devtools:ansible.com). - -For the full list of Ansible IRC and Mailing list, please see the -[Ansible Communication] page. -Release announcements will be made to the [Ansible Announce] list. - -Possible security bugs should be reported via email -to . - -## Code of Conduct - -As with all Ansible projects, we have a [Code of Conduct]. - -[.flake8]: https://github.com/ansible/ansible-lint/blob/main/.flake8 -[ansible announce]: https://groups.google.com/forum/#!forum/ansible-announce -[ansible communication]: https://docs.ansible.com/ansible/latest/community/communication.html -[code of conduct]: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html -[creating your fork on github]: https://guides.github.com/activities/forking/ -[discussions]: https://github.com/ansible/ansible-lint/discussions -[supported ansible versions]: https://docs.ansible.com/ansible-core/devel/reference_appendices/release_and_maintenance.html#ansible-core-release-cycle -[tox]: https://tox.readthedocs.io diff --git a/.gitignore b/.gitignore index 81fb714dcc..e082ca2b0c 100644 --- a/.gitignore +++ b/.gitignore @@ -64,7 +64,8 @@ src/ansiblelint/_version.py .pytest_cache test/eco/CODENOTIFY.html test/eco -docs/profiles.md test/schemas/node_modules .envrc collections +site +_readthedocs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0bdd002602..57006b3a00 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -198,7 +198,6 @@ repos: - pyyaml - rich>=13.2.0 - ruamel.yaml - - sphinx - typing_extensions - wcmatch - yamllint diff --git a/.readthedocs.yml b/.readthedocs.yml index 1cfd9b82ec..ac4348604c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,52 +1,23 @@ -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html -# for details - --- -# Required version: 2 -# Build documentation in the docs/ directory with Sphinx -sphinx: - # keep dirhtml for nice URLs without .html extension - builder: dirhtml - configuration: docs/conf.py +mkdocs: fail_on_warning: true - -# Build documentation with MkDocs -#mkdocs: -# configuration: mkdocs.yml -# fail_on_warning: true - -# Optionally build your docs in additional formats -# such as PDF and ePub -formats: [] - -submodules: - include: all # [] - exclude: [] - recursive: true + configuration: mkdocs.yml build: - # when using pre_build, "image:" is not supported and os and tools are required os: ubuntu-22.04 tools: - python: "3.10" - jobs: - pre_build: - - pip install '.[docs,test]' - - ansible-lint -L -f docs -# Optionally set the version of Python and requirements required -# to build docs -# python: -# version: "3.9" -# install: -# # On https://readthedocs.org/dashboard/ansible-lint/environmentvariables/ we -# # do have PIP_CONSTRAINTS=.config/requirements.txt which ensures we install only -# # pinned requirements that that we know to be working. -# - method: pip -# path: . -# extra_requirements: -# - docs -# - test -# system_packages: false + python: "3.11" + commands: + - pip install --user tox + - python3 -m tox -e docs -- --strict --site-dir=_readthedocs/html/ +python: + system_packages: false + install: + - method: pip + path: tox + - method: pip + path: . + extra_requirements: + - docs diff --git a/a.ansi b/a.ansi new file mode 100644 index 0000000000..62588d28c6 --- /dev/null +++ b/a.ansi @@ -0,0 +1 @@ +Hello World diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index b84f66817e..0000000000 --- a/docs/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Documentation source for Ansible Lint - -To build the docs, run `tox -e docs`. At the end of the build, you will -see the local location of your built docs. - -Building docs locally may not be identical to CI/CD builds. We recommend -you to create a draft PR and check the RTD PR preview page too. - -If you do not want to learn the reStructuredText format, you can also [file an issue](https://github.com/ansible/ansible-lint/issues), and let us know how we can improve our documentation. diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 98eee82896..0000000000 --- a/docs/conf.py +++ /dev/null @@ -1,338 +0,0 @@ -# -# documentation build configuration file, created by -# sphinx-quickstart on Sat Sep 27 13:23:22 2008-2009. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed -# automatically). -# -# All configuration values have a default value; values that are commented out -# serve to show the default value. -"""Documentation Configuration.""" -# pylint: disable=invalid-name -from __future__ import annotations - -import os -import sys -from pathlib import Path - -import pkg_resources - -# Make in-tree extension importable in non-tox setups/envs, like RTD. -# Refs: -# https://github.com/readthedocs/readthedocs.org/issues/6311 -# https://github.com/readthedocs/readthedocs.org/issues/7182 -sys.path.insert(0, str(Path(__file__).parent.resolve())) - -# pip3 install sphinx_rtd_theme -# import sphinx_rtd_theme -# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. -# sys.path.append(os.path.abspath('some/directory')) -# -sys.path.insert(0, os.path.join("ansible", "lib")) -sys.path.append(os.path.abspath("_themes")) - -VERSION = "latest" -AUTHOR = "Ansible, Inc" - -# General configuration -# --------------------- - -# Add any Sphinx extension module names here, as strings. -# They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -# TEST: 'sphinxcontrib.fulltoc' -extensions = [ - "myst_parser", - "sphinx.ext.autodoc", - "sphinx.ext.intersphinx", - # Third-party extensions: - "sphinxcontrib.apidoc", - "sphinxcontrib.programoutput", -] - - -# Fail safe protection to detect conflicting packages -try: - pkg_resources.get_distribution("sphinxcontrib-programoutput") - # flake8: noqa: T201 - print( - "FATAL: We detected presence of sphinxcontrib-programoutput package instead of sphinxcontrib-programoutput2 one. You must be sure the first is not installed.", - file=sys.stderr, - ) - sys.exit(2) -except pkg_resources.DistributionNotFound: - pass - -# Later on, add 'sphinx.ext.viewcode' to the list if you want to have -# colorized code generated too for references. - - -# Add any paths that contain templates here, relative to this directory. -templates_path = [".templates"] - -# The suffix of source filenames. -source_suffix = { - ".rst": "restructuredtext", - ".md": "markdown", -} - -# The master toctree document. -master_doc = "index" - -apidoc_excluded_paths: list[str] = [] -apidoc_extra_args = [ - "--implicit-namespaces", - "--private", # include “_private” modules -] -apidoc_module_dir = "../src/ansiblelint" -apidoc_module_first = False -apidoc_output_dir = "pkg" -apidoc_separate_modules = True -apidoc_toc_file: str | None = None - -# General substitutions. -project = "Ansible Lint Documentation" -copyright = "Ansible Lint project contributors" # pylint: disable=redefined-builtin - -github_url = "https://github.com" -github_repo_org = "ansible" -github_repo_name = "ansible-lint" -github_repo_slug = f"{github_repo_org}/{github_repo_name}" -github_repo_url = f"{github_url}/{github_repo_slug}" - -extlinks = { - "issue": (f"{github_repo_url}/issues/%s", "#"), - "pr": (f"{github_repo_url}/pull/%s", "PR #"), - "commit": (f"{github_repo_url}/commit/%s", ""), - "gh": (f"{github_url}/%s", "GitHub: "), -} - -intersphinx_mapping = { - "ansible": ("https://docs.ansible.com/ansible/devel/", None), - "ansible-core": ("https://docs.ansible.com/ansible-core/devel/", None), - "packaging": ("https://packaging.pypa.io/en/latest", None), - "pytest": ("https://docs.pytest.org/en/latest", None), - "python": ("https://docs.python.org/3", None), - "rich": ("https://rich.readthedocs.io/en/stable", None), -} - -# The default replacements for |version| and |release|, also used in various -# other places throughout the built documents. -# -# The short X.Y version. -version = VERSION -# The full version, including alpha/beta/rc tags. -release = VERSION - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -today_fmt = "%F" # ISO date format - -# List of documents that shouldn't be included in the build. -# unused_docs = [] - -# List of directories, relative to source directories, that shouldn't be -# searched for source files. -# exclude_dirs = [] - -# A list of glob-style patterns that should be excluded when looking -# for source files. -# OBSOLETE - removing this - dharmabumstead 2018-02-06 -exclude_patterns = ["README.md"] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -default_role = "any" - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = "ansible" - -highlight_language = "YAML+Jinja" - -# Options for HTML output -# ----------------------- - -html_theme_path = ["../_themes"] -html_theme = "sphinx_ansible_theme" - -html_theme_options = { - "collapse_navigation": False, - "analytics_id": "UA-128382387-1", - # cspell:disable-next-line - "tag_manager_id": "GTM-5FGNF6S", - "style_nav_header_background": "#5bbdbf", - "style_external_links": True, - # 'canonical_url': "https://docs.ansible.com/ansible/latest/", - "vcs_pageview_mode": "edit", - "navigation_depth": 3, - "display_version": False, - "logo_only": True, -} - -html_context = { - "display_github": "True", - "github_user": "ansible", - "github_repo": "ansible-lint", - "github_version": "main/docs/", - "current_version": version, - "latest_version": "latest", - # list specifically out of order to make latest work - "available_versions": ("latest",), -} - -# This appears on the left side of the page, in the header bar -html_short_title = "Ansible Lint Documentation" - -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. -# html_style = 'solar.css' - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -html_title = "Ansible Lint Documentation" - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (within the static path) to place at the top of -# the sidebar. -# -# ssbarnea: Do not put relative path because it will not load from some deeper -# pages as the relative path will be wrong, probably a bug in our schema. -html_logo = "_static/ansible-lint.svg" - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# -# ssbarnea: Do not put SVG or PND here due to limited browser support. The -# value is relative to config file! -html_favicon = "_static/images/favicon.ico" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['.static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -html_last_updated_fmt = "%b %d, %Y" - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_use_modindex = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, the reST sources are included in the HTML build as _sources/. -html_copy_source = False - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -html_use_opensearch = "https://ansible-lint.readthedocs.io/" - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = '' - -autoclass_content = "both" - -# table width fix via: https://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html -html_static_path = ["_static"] - -html_css_files = [ # relative to html_static_path - "theme_overrides.css", # override wide tables in RTD theme - "ansi.css", -] - -linkcheck_workers = 25 - -# Matrix room links look like they have anchors -linkcheck_anchors_ignore = [ - "^!", - "^/#[a-z]+:ansible\\.com$", -] - -nitpicky = True -nitpick_ignore = [ - ("py:class", "AnsibleBaseYAMLObject"), - ("py:class", "ansible.module_utils.basic.AnsibleModule"), - ("py:class", "ansible.plugins.loader.PluginLoadContext"), - ("py:class", "BasePathLike"), - ("py:class", "CommentedMap"), - ("py:class", "CommentedSeq"), - ("py:class", "CompletedProcess"), - ("py:class", "FileType"), - ("py:class", "LintResult"), - ("py:class", "Lintable"), - ("py:class", "MatchError"), - ("py:class", "Namespace"), - ("py:class", "Path"), - ("py:class", "Pattern"), - ("py:class", "RulesCollection"), - ("py:class", "StreamType"), # used in Emitter's type annotation - ("py:class", "Templar"), - ("py:class", "_pytest.fixtures.SubRequest"), - ("py:class", "ansible.parsing.yaml.objects.AnsibleBaseYAMLObject"), - ("py:class", "ansible.template.Templar"), - ("py:class", "handlers"), - ("py:class", "meta"), - ("py:class", "playbook"), - ("py:class", "re.Pattern"), - ("py:class", "requirements"), - ("py:class", "role"), - ("py:class", "ruamel.yaml.comments.CommentedMap"), - ("py:class", "ruamel.yaml.comments.CommentedSeq"), - ("py:class", "ruamel.yaml.constructor.RoundTripConstructor"), - ("py:class", "ruamel.yaml.emitter.Emitter"), - ("py:class", "ruamel.yaml.emitter.ScalarAnalysis"), - ("py:class", "ruamel.yaml.main.YAML"), - ("py:class", "ruamel.yaml.nodes.ScalarNode"), - ("py:class", "ruamel.yaml.representer.RoundTripRepresenter"), - ("py:class", "ruamel.yaml.scalarint.ScalarInt"), - ("py:class", "ruamel.yaml.tokens.CommentToken"), - ("py:class", "tasks"), - ("py:class", "yaml"), - ("py:class", "yamllint.config.YamlLintConfig"), - ("py:obj", "Any"), - ("py:obj", "ansiblelint.formatters.T"), -] - -myst_heading_anchors = 3 -myst_ref_domains = ("std", "py") diff --git a/docs/configuring.md b/docs/configuring.md index 4976baed76..49deb3d4e3 100644 --- a/docs/configuring.md +++ b/docs/configuring.md @@ -1,29 +1,29 @@ -(configuring-ansible-lint)= - # Configuration -```{contents} Topics - -``` - Customize how Ansible-lint runs against automation content to suit your needs. -You can ignore certain rules, enable `opt-in` rules, and control various other settings. +You can ignore certain rules, enable `opt-in` rules, and control various other +settings. -Ansible-lint loads configuration from a file in the current working directory or from a file that you specify in the command line. -If you provide configuration on both via a config file and on the command line, list values are merged (for example `exclude_paths`) and **True** is preferred for boolean values like `quiet`. +Ansible-lint loads configuration from a file in the current working directory or +from a file that you specify in the command line. If you provide configuration +on both via a config file and on the command line, list values are merged (for +example `exclude_paths`) and **True** is preferred for boolean values like +`quiet`. ## Using local configuration files -Specify Ansible-lint configuration in either `.ansible-lint` or `.config/ansible-lint.yml` in your current working directory. +Specify Ansible-lint configuration in either `.ansible-lint` or +`.config/ansible-lint.yml` in your current working directory. -```{note} -If Ansible-lint cannot find a configuration file in the current directory it attempts to locate it in a parent directory. -However Ansible-lint does not try to load configuration that is outside the git repository. -``` +!!! note + + If Ansible-lint cannot find a configuration file in the current directory it attempts to locate it in a parent directory. + However Ansible-lint does not try to load configuration that is outside the git repository. ## Specifying configuration files -Use the `-c ` CLI flag with command line invocations of Ansible-lint, for example: +Use the `-c ` CLI flag with command line invocations of Ansible-lint, +for example: ```bash ansible-lint -c path/to/ansible-lint-dev.yml @@ -34,15 +34,17 @@ ansible-lint -c path/to/ansible-lint-dev.yml The following values are supported, and function identically to their CLI counterparts: -```{literalinclude} ../.ansible-lint -:language: yaml +```yaml +--8<-- ".ansible-lint" ``` ## Pre-commit setup -To use Ansible-lint with [pre-commit], add the following to the `.pre-commit-config.yaml` file in your local repository. +To use Ansible-lint with [pre-commit], add the following to the +`.pre-commit-config.yaml` file in your local repository. -Change **rev:** to either a commit sha or tag of Ansible-lint that contains `.pre-commit-hooks.yaml`. +Change **rev:** to either a commit sha or tag of Ansible-lint that contains +`.pre-commit-hooks.yaml`. ```yaml - repo: https://github.com/ansible/ansible-lint.git diff --git a/docs/contributing.md b/docs/contributing.md index 91e0091101..ebd19711c1 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,33 +1,94 @@ -```{include} ../.github/CONTRIBUTING.md - :end-before: DO-NOT-REMOVE-deps-snippet-PLACEHOLDER +# Contributing to Ansible-lint + +To contribute to ansible-lint, please use pull requests on a branch of your own +fork. + +After [creating your fork on GitHub], you can do: + +```shell-session +$ git clone git@github.com:your-name/ansible-lint +$ cd ansible-lint +$ git checkout -b your-branch-name +# DO SOME CODING HERE +$ git add your new files +$ git commit -v +$ git push origin your-branch-name ``` -# Module dependency graph +You will then be able to create a pull request from your commit. + +All fixes to core functionality (i.e. anything except docs or examples) should +be accompanied by tests that fail prior to your change and succeed afterwards. + +Feel free to raise issues in the repo if you feel unable to contribute a code +fix. + +## Standards + +ansible-lint is flake8 compliant with **max-line-length** set to 100. + +ansible-lint works only with supported Ansible versions at the time it was +released. + +Automated tests will be run against all PRs for flake8 compliance and Ansible +compatibility — to check before pushing commits, just use +[tox](https://tox.wiki/en/latest/). + +% DO-NOT-REMOVE-deps-snippet-PLACEHOLDER + +## Talk to us -Extra care should be taken when considering adding any dependency. Removing -most dependencies on Ansible internals is desired as these can change -without any warning. +Use Github [discussions] forum or for a live chat experience try +`#ansible-devtools` IRC channel on libera.chat or Matrix room +[#devtools:ansible.com](https://matrix.to/#/#devtools:ansible.com). -```{command-output} _PIP_USE_IMPORTLIB_METADATA=0 pipdeptree -p ansible-lint - :shell: +For the full list of Ansible IRC and Mailing list, please see the [Ansible +Communication] page. Release announcements will be made to the [Ansible +Announce] list. +Possible security bugs should be reported via email to +. + +## Code of Conduct + +As with all Ansible projects, we have a [Code of Conduct]. + +[.flake8]: https://github.com/ansible/ansible-lint/blob/main/.flake8 +[ansible announce]: https://groups.google.com/forum/#!forum/ansible-announce +[ansible communication]: + https://docs.ansible.com/ansible/latest/community/communication.html +[code of conduct]: + https://docs.ansible.com/ansible/latest/community/code_of_conduct.html +[creating your fork on github]: https://guides.github.com/activities/forking/ +[discussions]: https://github.com/ansible/ansible-lint/discussions +[supported ansible versions]: + https://docs.ansible.com/ansible-core/devel/reference_appendices/release_and_maintenance.html#ansible-core-release-cycle +[tox]: https://tox.readthedocs.io + +## Module dependency graph + +Extra care should be taken when considering adding any dependency. Removing most +dependencies on Ansible internals is desired as these can change without any +warning. + +```bash exec="1" source="console" +_PIP_USE_IMPORTLIB_METADATA=0 pipdeptree -p ansible-lint ``` -# Adding a new rule +## Adding a new rule Writing a new rule is as easy as adding a single new rule, one that combines **implementation, testing and documentation**. -One good example is [MetaTagValidRule] which can easily be copied in order -to create a new rule by following the steps below: +One good example is [MetaTagValidRule] which can easily be copied in order to +create a new rule by following the steps below: - Use a short but clear class name, which must match the filename -- Pick an unused `id`, the first number is used to determine rule section. - Look at [rules](rules.md) page and pick one that matches the best - your new rule and ee which one fits best. -- Include `experimental` tag. Any new rule must stay as - experimental for at least two weeks until this tag is removed in next major - release. +- Pick an unused `id`, the first number is used to determine rule section. Look + at [rules](rules/index.md) page and pick one that matches the best your new + rule and ee which one fits best. +- Include `experimental` tag. Any new rule must stay as experimental for at + least two weeks until this tag is removed in next major release. - Update all class level variables. - Implement linting methods needed by your rule, these are those starting with **match** prefix. Implement only those you need. For the moment you will need @@ -45,4 +106,17 @@ to create a new rule by following the steps below: - Build the docs using {command}`tox -e docs` and check that the new rule is displayed correctly in them. -[metatagvalidrule]: https://github.com/ansible/ansible-lint/blob/main/src/ansiblelint/rules/meta_no_tags.py +[metatagvalidrule]: + https://github.com/ansible/ansible-lint/blob/main/src/ansiblelint/rules/meta_no_tags.py + +## Documentation changes + +To build the docs, run `tox -e docs`. At the end of the build, you will see the +local location of your built docs. + +Building docs locally may not be identical to CI/CD builds. We recommend you to +create a draft PR and check the RTD PR preview page too. + +If you do not want to learn the reStructuredText format, you can also +[file an issue](https://github.com/ansible/ansible-lint/issues), and let us know +how we can improve our documentation. diff --git a/docs/custom-rules.md b/docs/custom-rules.md index b752b758b7..8c57d90c31 100644 --- a/docs/custom-rules.md +++ b/docs/custom-rules.md @@ -1,16 +1,15 @@ -(defining-custom-rules)= - # Custom linting rules Define and use your own sets of rules with Ansible-lint. ## Rule definitions -You define each custom rule in a unique Python class file. -Default rules are named _DeprecatedVariableRule.py_, etc. +You define each custom rule in a unique Python class file. Default rules are +named _DeprecatedVariableRule.py_, etc. -Each rule should have a short description as a Python docstring wrapped in triple quotes `"""` immediately after the class name. -The short description should be brief and meaningfully explain the purpose of the rule to users. +Each rule should have a short description as a Python docstring wrapped in +triple quotes `"""` immediately after the class name. The short description +should be brief and meaningfully explain the purpose of the rule to users. Each rule definition should have the following parts: @@ -24,8 +23,13 @@ Each rule definition should also invoke one of the following methods: - `match` takes a line and returns: - None or False if the line does not match the test. - - True or a custom message if the line does match the test. (This allows one rule to test multiple behaviors - see e.g. the _CommandsInsteadOfModulesRule_.) -- `matchtask` operates on a single task or handler, such that tasks get standardized to always contain a _module_ key and _module_arguments_ key. Other common task modifiers, such as _when_, _with_items_, etc., are also available as keys if present in the task. + - True or a custom message if the line does match the test. (This allows one + rule to test multiple behaviors - see e.g. the + _CommandsInsteadOfModulesRule_.) +- `matchtask` operates on a single task or handler, such that tasks get + standardized to always contain a _module_ key and _module_arguments_ key. + Other common task modifiers, such as _when_, _with_items_, etc., are also + available as keys if present in the task. The following is an example rule that uses the `match` method: @@ -75,10 +79,10 @@ class TaskHasTag(AnsibleLintRule): return False ``` -The task argument to `matchtask` contains a number of keys - the critical -one is _action_. The value of `task['action']` contains the module being used, -and the arguments passed, both as key-value pairs and a list of other arguments -(e.g. the command used with shell). +The task argument to `matchtask` contains a number of keys - the critical one is +_action_. The value of `task['action']` contains the module being used, and the +arguments passed, both as key-value pairs and a list of other arguments (e.g. +the command used with shell). In ansible-lint 2.0.0, `task['action']['args']` was renamed `task['action']['module_arguments']` to avoid a clash when a module actually @@ -86,13 +90,14 @@ takes args as a parameter key (e.g. ec2_tag) In ansible-lint 3.0.0 `task['action']['module']` was renamed `task['action']['__ansible_module__']` to avoid a clash when a module take -module as an argument. As a precaution, `task['action']['module_arguments']` -was renamed `task['action']['__ansible_arguments__']`. +module as an argument. As a precaution, `task['action']['module_arguments']` was +renamed `task['action']['__ansible_arguments__']`. ## Packaging custom rules -Ansible-lint automatically loads and enables custom rules in Python packages from the _custom_ subdirectory. -This subdirectory is part of the Ansible-lint installation directory, for example: +Ansible-lint automatically loads and enables custom rules in Python packages +from the _custom_ subdirectory. This subdirectory is part of the Ansible-lint +installation directory, for example: `/usr/lib/python3.8/site-packages/ansiblelint/rules/custom/` @@ -100,7 +105,8 @@ To automatically load custom rules, do the following: 1. Package your custom rules as a Python package with a descriptive name. -2. Configure the \[options\] section of the `setup.cfg` of your custom rules Python package as in the following example: +2. Configure the \[options\] section of the `setup.cfg` of your custom rules + Python package as in the following example: ```yaml [options] @@ -110,4 +116,5 @@ To automatically load custom rules, do the following: ansiblelint.rules.custom. = ``` -3. Install the Python package into `/custom//`. +3. Install the Python package into + `/custom//`. diff --git a/docs/images/favicon.ico b/docs/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ea4ebc153b34e8074562dceb88d065afa80513a0 GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f3626b0ae4b13e32725a2577c46b520b6f14e91a GIT binary patch literal 10993 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 + + + + + + diff --git a/docs/index.md b/docs/index.md index 18df0e3af7..a800ff0dee 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,52 +1,28 @@ -(lint-documentation)= - # Ansible Lint Documentation ## About Ansible Lint Ansible Lint is a command-line tool for linting **playbooks, roles and -collections** aimed toward any Ansible users. Its main goal is to promote -proven practices, patterns and behaviors while avoiding common pitfalls that -can easily lead to bugs or make code harder to maintain. +collections** aimed toward any Ansible users. Its main goal is to promote proven +practices, patterns and behaviors while avoiding common pitfalls that can easily +lead to bugs or make code harder to maintain. Ansible lint is also supposed to help users upgrade their code to work with -newer versions of Ansible. Due to this reason we recommend using it with -the newest version of Ansible, even if the version used in production may be -older. +newer versions of Ansible. Due to this reason we recommend using it with the +newest version of Ansible, even if the version used in production may be older. As any other linter, it is opinionated. Still, its rules are the result of -community contributions and they can always be disabled based individually or -by category by each user. - -[Ansible Galaxy project](https://github.com/ansible/galaxy/) makes use of -this linter to compute quality scores for [Galaxy Hub](https://galaxy.ansible.com) -contributed content. This does not mean this tool only targets those -that want to share their code. Files like `galaxy.yml`, or sections like -`galaxy_info` inside `meta.yml` help with documentation and maintenance, -even for unpublished roles or collections. - -The project was originally started by [@willthames](https://github.com/willthames/) -and has since been adopted by the Ansible Community team. Its development is -purely community driven while keeping permanent communications with other -Ansible teams. - -```{toctree} -:caption: User Guide -:maxdepth: 1 - -Philosophy -installing -usage -configuring -using-profiles -rules -``` - -```{toctree} -:caption: Developer Guide -:maxdepth: 1 - -Contributing -custom-rules -Private API -``` +community contributions and they can always be disabled based individually or by +category by each user. + +[Ansible Galaxy project](https://github.com/ansible/galaxy/) makes use of this +linter to compute quality scores for [Galaxy Hub](https://galaxy.ansible.com) +contributed content. This does not mean this tool only targets those that want +to share their code. Files like `galaxy.yml`, or sections like `galaxy_info` +inside `meta.yml` help with documentation and maintenance, even for unpublished +roles or collections. + +The project was originally started by +[@willthames](https://github.com/willthames/) and has since been adopted by the +Ansible Community team. Its development is purely community driven while keeping +permanent communications with other Ansible teams. diff --git a/docs/installing.md b/docs/installing.md index b63731f9e8..026b580d68 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -1,38 +1,33 @@ -(installing-lint)= - # Installing -```{contents} Topics - -``` - Install Ansible-lint to apply rules and follow best practices with your automation content. -```{note} -Ansible-lint does not currently support installation on Windows systems. -``` +!!! note -```{warning} -Ansible-lint does not support any installation methods that are not -mentioned in this document. Before raising any bugs related to installation, -review all of the following details: - -- You should use installation methods outlined in this document only. -- You should upgrade the Python installer (`pip` or `pipx`) to the latest version - available from pypi.org. If you used a system package manager, you - will need to upgrade the installer to a newer version. -- If you are installing from a git zip archive, which is not supported but should work, - ensure you use the main branch and the latest version of pip and setuptools. -- If you are installing Ansible-lint within a container or system package, you - should not report the issue here. Contact the relevant container or package - provider instead. -- If you are using [poetry](https://python-poetry.org/), read this - [discussion](https://github.com/ansible/ansible-lint/discussions/2820#discussioncomment-4400380). - -Pull requests to improve installation instructions are welcome. Any new issues related to -installation will be closed and locked. -``` + Ansible-lint does not currently support installation on Windows systems. + +!!! warning + + Ansible-lint does not support any installation methods that are not mentioned in + this document. Before raising any bugs related to installation, review all of + the following details: + + - You should use installation methods outlined in this document only. + - You should upgrade the Python installer (`pip` or `pipx`) to the latest + version available from pypi.org. If you used a system package manager, you + will need to upgrade the installer to a newer version. + - If you are installing from a git zip archive, which is not supported but + should work, ensure you use the main branch and the latest version of pip and + setuptools. + - If you are installing Ansible-lint within a container or system package, you + should not report the issue here. Contact the relevant container or package + provider instead. + - If you are using [poetry](https://python-poetry.org/), read this + [discussion](https://github.com/ansible/ansible-lint/discussions/2820#discussioncomment-4400380). + + Pull requests to improve installation instructions are welcome. Any new issues + related to installation will be closed and locked. For a container image, we recommend using [creator-ee](https://github.com/ansible/creator-ee/), which includes @@ -52,7 +47,7 @@ current Python environment as an alternative to creating a virtual environment. ```bash # This also installs ansible-core if it is not already installed -pip3 install "ansible-lint" +pip3 install ansible-lint ``` ## Installing on Fedora and RHEL @@ -64,10 +59,10 @@ the `dnf` package manager. dnf install ansible-lint ``` -```{note} -On RHEL, `ansible-lint` package is part of "Red Hat Ansible Automation Platform" subscription, which needs -to be activated. -``` +!!! note + + On RHEL, `ansible-lint` package is part of "Red Hat Ansible Automation + Platform" subscription, which needs to be activated. ## Installing from source code diff --git a/docs/profiles.md b/docs/profiles.md new file mode 100644 index 0000000000..d105ddb136 --- /dev/null +++ b/docs/profiles.md @@ -0,0 +1,113 @@ + + +# Profiles + +Ansible-lint profiles gradually increase the strictness of rules as your Ansible +content lifecycle. To configure linter to use a specific profile, read +[applying-profiles][]. + +!!! note + + Rules with `*` in the suffix are not yet implemented but are documented with linked GitHub issues. + +## min + +The `min` profile ensures that Ansible can load content. Rules in this profile +are mandatory because they prevent fatal errors. You can add files to the +exclude list or provide dependencies to load the correct files. + +- [internal-error](rules/internal-error/) +- [load-failure](rules/load-failure/) +- [parser-error](rules/parser-error/) +- [syntax-check](rules/syntax-check/) + +## basic + +The `basic` profile prevents common coding issues and enforces standard styles +and formatting. It extends [min](#min) profile. + +- [command-instead-of-module](rules/command-instead-of-module/) +- [command-instead-of-shell](rules/command-instead-of-shell/) +- [deprecated-bare-vars](rules/deprecated-bare-vars/) +- [deprecated-command-syntax](rules/deprecated-command-syntax/) +- [deprecated-local-action](rules/deprecated-local-action/) +- [deprecated-module](rules/deprecated-module/) +- [inline-env-var](rules/inline-env-var/) +- [key-order](rules/key-order/) +- [literal-compare](rules/literal-compare/) +- [jinja](rules/jinja/) +- [no-jinja-when](rules/no-jinja-when/) +- [no-tabs](rules/no-tabs/) +- [partial-become](rules/partial-become/) +- [playbook-extension](rules/playbook-extension/) +- [role-name](rules/role-name/) +- [schema](rules/schema/) +- [name](rules/name/) +- [var-naming](rules/var-naming/) +- [yaml](rules/yaml/) + +## moderate + +The `moderate` profile ensures that content adheres to best practices for making +content easier to read and maintain. It extends [basic](#basic) profile. + +- [name[template]](rules/name/) +- [name[imperative]](https://github.com/ansible/ansible-lint/issues/2170) +- [name[casing]](rules/name/) +- [no-free-form](https://github.com/ansible/ansible-lint/issues/2117) +- [spell-var-name](https://github.com/ansible/ansible-lint/issues/2168) + +## safety + +The `safety` profile avoids module calls that can have non-determinant outcomes +or security concerns. It extends [moderate](#moderate) profile. + +- [avoid-implicit](rules/avoid-implicit/) +- [latest](rules/latest/) +- [package-latest](rules/package-latest/) +- [risky-file-permissions](rules/risky-file-permissions/) +- [risky-octal](rules/risky-octal/) +- [risky-shell-pipe](rules/risky-shell-pipe/) + +## shared + +The `shared` profile ensures that content follows best practices for packaging +and publishing. This profile is intended for content creators who want to make +Ansible playbooks, roles, or collections available from +[galaxy.ansible.com](https://galaxy.ansible.com), +[automation-hub](https://console.redhat.com/ansible/automation-hub), or a +private instance. It extends [safety](#safety) profile. + +- [galaxy](rules/galaxy/) +- [ignore-errors](rules/ignore-errors/) +- [layout](https://github.com/ansible/ansible-lint/issues/1900) +- [meta-incorrect](rules/meta-incorrect/) +- [meta-no-info](rules/meta-no-info/) +- [meta-no-tags](rules/meta-no-tags/) +- [meta-video-links](rules/meta-video-links/) +- [meta-version](https://github.com/ansible/ansible-lint/issues/2103) +- [meta-unsupported-ansible](https://github.com/ansible/ansible-lint/issues/2102) +- [no-changed-when](rules/no-changed-when/) +- [no-changelog](https://github.com/ansible/ansible-lint/issues/2101) +- [no-handler](rules/no-handler/) +- [no-relative-paths](rules/no-relative-paths/) +- [max-block-depth](https://github.com/ansible/ansible-lint/issues/2173) +- [max-tasks](https://github.com/ansible/ansible-lint/issues/2172) +- [unsafe-loop](https://github.com/ansible/ansible-lint/issues/2038) + +## production + +The `production` profile ensures that content meets requirements for inclusion +in +[Ansible Automation Platform (AAP)](https://www.redhat.com/en/technologies/management/ansible) +as validated or certified content. It extends [shared](#shared) profile. + +- [avoid-dot-notation](https://github.com/ansible/ansible-lint/issues/2174) +- [disallowed-ignore](https://github.com/ansible/ansible-lint/issues/2121) +- [fqcn](rules/fqcn/) +- [import-task-no-when](https://github.com/ansible/ansible-lint/issues/2219) +- [meta-no-dependencies](https://github.com/ansible/ansible-lint/issues/2159) +- [single-entry-point](https://github.com/ansible/ansible-lint/issues/2242) +- [use-loop](https://github.com/ansible/ansible-lint/issues/2204) diff --git a/docs/rules.md b/docs/rules.md deleted file mode 100644 index a0f942ec87..0000000000 --- a/docs/rules.md +++ /dev/null @@ -1,10 +0,0 @@ -(lint-rules)= - -# Rules - -```{toctree} -:maxdepth: 1 -:glob: - -rules/* -``` diff --git a/docs/rules/.gitignore b/docs/rules/.gitignore deleted file mode 100644 index 9393a70fdb..0000000000 --- a/docs/rules/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Generated files -*.md diff --git a/docs/rules/args.md b/docs/rules/args.md new file mode 120000 index 0000000000..194c2d3329 --- /dev/null +++ b/docs/rules/args.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/args.md \ No newline at end of file diff --git a/docs/rules/avoid-implicit.md b/docs/rules/avoid-implicit.md new file mode 120000 index 0000000000..4ddfc8237c --- /dev/null +++ b/docs/rules/avoid-implicit.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/avoid_implicit.md \ No newline at end of file diff --git a/docs/rules/command-instead-of-module.md b/docs/rules/command-instead-of-module.md new file mode 120000 index 0000000000..9f6809d903 --- /dev/null +++ b/docs/rules/command-instead-of-module.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/command_instead_of_module.md \ No newline at end of file diff --git a/docs/rules/command-instead-of-shell.md b/docs/rules/command-instead-of-shell.md new file mode 120000 index 0000000000..2bf0747ce0 --- /dev/null +++ b/docs/rules/command-instead-of-shell.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/command_instead_of_shell.md \ No newline at end of file diff --git a/docs/rules/deprecated-bare-vars.md b/docs/rules/deprecated-bare-vars.md new file mode 120000 index 0000000000..80ca8f7df9 --- /dev/null +++ b/docs/rules/deprecated-bare-vars.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/deprecated_bare_vars.md \ No newline at end of file diff --git a/docs/rules/deprecated-command-syntax.md b/docs/rules/deprecated-command-syntax.md new file mode 120000 index 0000000000..70cb557401 --- /dev/null +++ b/docs/rules/deprecated-command-syntax.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/deprecated_command_syntax.md \ No newline at end of file diff --git a/docs/rules/deprecated-local-action.md b/docs/rules/deprecated-local-action.md new file mode 120000 index 0000000000..fd44cd8d78 --- /dev/null +++ b/docs/rules/deprecated-local-action.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/deprecated_local_action.md \ No newline at end of file diff --git a/docs/rules/deprecated-module.md b/docs/rules/deprecated-module.md new file mode 120000 index 0000000000..28dfe7fdbc --- /dev/null +++ b/docs/rules/deprecated-module.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/deprecated_module.md \ No newline at end of file diff --git a/docs/rules/empty-string-compare.md b/docs/rules/empty-string-compare.md new file mode 120000 index 0000000000..3470d52774 --- /dev/null +++ b/docs/rules/empty-string-compare.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/empty_string_compare.md \ No newline at end of file diff --git a/docs/rules/fqcn.md b/docs/rules/fqcn.md new file mode 120000 index 0000000000..5623faeaab --- /dev/null +++ b/docs/rules/fqcn.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/fqcn.md \ No newline at end of file diff --git a/docs/rules/galaxy.md b/docs/rules/galaxy.md new file mode 120000 index 0000000000..02cebd0eac --- /dev/null +++ b/docs/rules/galaxy.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/galaxy.md \ No newline at end of file diff --git a/docs/rules/ignore-errors.md b/docs/rules/ignore-errors.md new file mode 120000 index 0000000000..ea5578d78f --- /dev/null +++ b/docs/rules/ignore-errors.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/ignore_errors.md \ No newline at end of file diff --git a/docs/rules/index.md b/docs/rules/index.md new file mode 100644 index 0000000000..68851813a9 --- /dev/null +++ b/docs/rules/index.md @@ -0,0 +1,52 @@ +# Rules + +- [args][] +- [avoid-implicit][] +- [command-instead-of-module][] +- [command-instead-of-shell][] +- [deprecated-bare-vars][] +- [deprecated-command-syntax][] +- [deprecated-local-action][] +- [deprecated-module][] +- [empty-string-compare][] +- [fqcn][] +- [galaxy][] +- [ignore-errors][] +- [inline-env-var][] +- [internal-error][] +- [jinja][] +- [key-order][] +- [latest][] +- [literal-compare][] +- [load-failure][] +- [loop-var-prefix][] +- [meta-incorrect][] +- [meta-no-info][] +- [meta-no-tags][] +- [meta-unsupported-ansible][] +- [meta-video-links][] +- [name][] +- [no-changed-when][] +- [no-free-form][] +- [no-handler][] +- [no-jinja-when][] +- [no-log-password][] +- [no-prompting][] +- [no-relative-paths][] +- [no-same-owner][] +- [no-tabs][] +- [only-builtins][] +- [package-latest][] +- [parser-error][] +- [partial-become][] +- [playbook-extension][] +- [risky-file-permissions][] +- [risky-octal][] +- [risky-shell-pipe][] +- [role-name][] +- [run-once][] +- [schema][] +- [syntax-check][] +- [var-naming][] +- [warning][] +- [yaml][] diff --git a/docs/rules/inline-env-var.md b/docs/rules/inline-env-var.md new file mode 120000 index 0000000000..8942b5c24b --- /dev/null +++ b/docs/rules/inline-env-var.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/inline_env_var.md \ No newline at end of file diff --git a/docs/rules/internal-error.md b/docs/rules/internal-error.md new file mode 120000 index 0000000000..828768550b --- /dev/null +++ b/docs/rules/internal-error.md @@ -0,0 +1 @@ +../../src/ansiblelint/_internal/internal_error.md \ No newline at end of file diff --git a/docs/rules/jinja.md b/docs/rules/jinja.md new file mode 120000 index 0000000000..6f5fc6e02a --- /dev/null +++ b/docs/rules/jinja.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/jinja.md \ No newline at end of file diff --git a/docs/rules/key-order.md b/docs/rules/key-order.md new file mode 120000 index 0000000000..929faa69ac --- /dev/null +++ b/docs/rules/key-order.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/key_order.md \ No newline at end of file diff --git a/docs/rules/latest.md b/docs/rules/latest.md new file mode 120000 index 0000000000..6280f1a808 --- /dev/null +++ b/docs/rules/latest.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/latest.md \ No newline at end of file diff --git a/docs/rules/literal-compare.md b/docs/rules/literal-compare.md new file mode 120000 index 0000000000..4a7ab236fb --- /dev/null +++ b/docs/rules/literal-compare.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/literal_compare.md \ No newline at end of file diff --git a/docs/rules/load-failure.md b/docs/rules/load-failure.md new file mode 120000 index 0000000000..38a66a68cb --- /dev/null +++ b/docs/rules/load-failure.md @@ -0,0 +1 @@ +../../src/ansiblelint/_internal/load-failure.md \ No newline at end of file diff --git a/docs/rules/loop-var-prefix.md b/docs/rules/loop-var-prefix.md new file mode 120000 index 0000000000..6cebf6aced --- /dev/null +++ b/docs/rules/loop-var-prefix.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/loop_var_prefix.md \ No newline at end of file diff --git a/docs/rules/meta-incorrect.md b/docs/rules/meta-incorrect.md new file mode 120000 index 0000000000..34c18915ca --- /dev/null +++ b/docs/rules/meta-incorrect.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/meta_incorrect.md \ No newline at end of file diff --git a/docs/rules/meta-no-info.md b/docs/rules/meta-no-info.md new file mode 120000 index 0000000000..1e9371577a --- /dev/null +++ b/docs/rules/meta-no-info.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/meta_no_info.md \ No newline at end of file diff --git a/docs/rules/meta-no-tags.md b/docs/rules/meta-no-tags.md new file mode 120000 index 0000000000..b103c5706b --- /dev/null +++ b/docs/rules/meta-no-tags.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/meta_no_tags.md \ No newline at end of file diff --git a/docs/rules/meta-unsupported-ansible.md b/docs/rules/meta-unsupported-ansible.md new file mode 120000 index 0000000000..8fcb1c74ef --- /dev/null +++ b/docs/rules/meta-unsupported-ansible.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/meta_unsupported_ansible.md \ No newline at end of file diff --git a/docs/rules/meta-video-links.md b/docs/rules/meta-video-links.md new file mode 120000 index 0000000000..176559db40 --- /dev/null +++ b/docs/rules/meta-video-links.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/meta_video_links.md \ No newline at end of file diff --git a/docs/rules/name.md b/docs/rules/name.md new file mode 120000 index 0000000000..213f9244bb --- /dev/null +++ b/docs/rules/name.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/name.md \ No newline at end of file diff --git a/docs/rules/no-changed-when.md b/docs/rules/no-changed-when.md new file mode 120000 index 0000000000..e821c776bd --- /dev/null +++ b/docs/rules/no-changed-when.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_changed_when.md \ No newline at end of file diff --git a/docs/rules/no-free-form.md b/docs/rules/no-free-form.md new file mode 120000 index 0000000000..ee80943cf0 --- /dev/null +++ b/docs/rules/no-free-form.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_free_form.md \ No newline at end of file diff --git a/docs/rules/no-handler.md b/docs/rules/no-handler.md new file mode 120000 index 0000000000..1b690c6de5 --- /dev/null +++ b/docs/rules/no-handler.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_handler.md \ No newline at end of file diff --git a/docs/rules/no-jinja-when.md b/docs/rules/no-jinja-when.md new file mode 120000 index 0000000000..d2f945348f --- /dev/null +++ b/docs/rules/no-jinja-when.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_jinja_when.md \ No newline at end of file diff --git a/docs/rules/no-log-password.md b/docs/rules/no-log-password.md new file mode 120000 index 0000000000..ac5e8698a7 --- /dev/null +++ b/docs/rules/no-log-password.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_log_password.md \ No newline at end of file diff --git a/docs/rules/no-prompting.md b/docs/rules/no-prompting.md new file mode 120000 index 0000000000..49b3d4b906 --- /dev/null +++ b/docs/rules/no-prompting.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_prompting.md \ No newline at end of file diff --git a/docs/rules/no-relative-paths.md b/docs/rules/no-relative-paths.md new file mode 120000 index 0000000000..6fc7d42119 --- /dev/null +++ b/docs/rules/no-relative-paths.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_relative_paths.md \ No newline at end of file diff --git a/docs/rules/no-same-owner.md b/docs/rules/no-same-owner.md new file mode 120000 index 0000000000..6367a7ad58 --- /dev/null +++ b/docs/rules/no-same-owner.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_same_owner.md \ No newline at end of file diff --git a/docs/rules/no-tabs.md b/docs/rules/no-tabs.md new file mode 120000 index 0000000000..d938e62586 --- /dev/null +++ b/docs/rules/no-tabs.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/no_tabs.md \ No newline at end of file diff --git a/docs/rules/only-builtins.md b/docs/rules/only-builtins.md new file mode 120000 index 0000000000..7fa5e7aa6f --- /dev/null +++ b/docs/rules/only-builtins.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/only_builtins.md \ No newline at end of file diff --git a/docs/rules/package-latest.md b/docs/rules/package-latest.md new file mode 120000 index 0000000000..18d6723a63 --- /dev/null +++ b/docs/rules/package-latest.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/package_latest.md \ No newline at end of file diff --git a/docs/rules/parser-error.md b/docs/rules/parser-error.md new file mode 120000 index 0000000000..954422ff1c --- /dev/null +++ b/docs/rules/parser-error.md @@ -0,0 +1 @@ +../../src/ansiblelint/_internal/parser-error.md \ No newline at end of file diff --git a/docs/rules/partial-become.md b/docs/rules/partial-become.md new file mode 120000 index 0000000000..f57aa23579 --- /dev/null +++ b/docs/rules/partial-become.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/partial_become.md \ No newline at end of file diff --git a/docs/rules/playbook-extension.md b/docs/rules/playbook-extension.md new file mode 120000 index 0000000000..429aa2fbb6 --- /dev/null +++ b/docs/rules/playbook-extension.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/playbook_extension.md \ No newline at end of file diff --git a/docs/rules/risky-file-permissions.md b/docs/rules/risky-file-permissions.md new file mode 120000 index 0000000000..a6718bf436 --- /dev/null +++ b/docs/rules/risky-file-permissions.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/risky_file_permissions.md \ No newline at end of file diff --git a/docs/rules/risky-octal.md b/docs/rules/risky-octal.md new file mode 120000 index 0000000000..dffaad5764 --- /dev/null +++ b/docs/rules/risky-octal.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/risky_octal.md \ No newline at end of file diff --git a/docs/rules/risky-shell-pipe.md b/docs/rules/risky-shell-pipe.md new file mode 120000 index 0000000000..444efa225c --- /dev/null +++ b/docs/rules/risky-shell-pipe.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/risky_shell_pipe.md \ No newline at end of file diff --git a/docs/rules/role-name.md b/docs/rules/role-name.md new file mode 120000 index 0000000000..e050ccdd2b --- /dev/null +++ b/docs/rules/role-name.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/role_name.md \ No newline at end of file diff --git a/docs/rules/run-once.md b/docs/rules/run-once.md new file mode 120000 index 0000000000..b238e258cd --- /dev/null +++ b/docs/rules/run-once.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/run_once.md \ No newline at end of file diff --git a/docs/rules/schema.md b/docs/rules/schema.md new file mode 120000 index 0000000000..a278289fcc --- /dev/null +++ b/docs/rules/schema.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/schema.md \ No newline at end of file diff --git a/docs/rules/syntax-check.md b/docs/rules/syntax-check.md new file mode 120000 index 0000000000..0e5e8f1e3a --- /dev/null +++ b/docs/rules/syntax-check.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/syntax_check.md \ No newline at end of file diff --git a/docs/rules/var-naming.md b/docs/rules/var-naming.md new file mode 120000 index 0000000000..50bc37b8be --- /dev/null +++ b/docs/rules/var-naming.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/var_naming.md \ No newline at end of file diff --git a/docs/rules/warning.md b/docs/rules/warning.md new file mode 120000 index 0000000000..9944a532ce --- /dev/null +++ b/docs/rules/warning.md @@ -0,0 +1 @@ +../../src/ansiblelint/_internal/warning.md \ No newline at end of file diff --git a/docs/rules/yaml.md b/docs/rules/yaml.md new file mode 120000 index 0000000000..b033a46114 --- /dev/null +++ b/docs/rules/yaml.md @@ -0,0 +1 @@ +../../src/ansiblelint/rules/yaml.md \ No newline at end of file diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000000..9b14d833eb --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,174 @@ +/* +Inspired by https://spec.draculatheme.com/ specification +*/ +:root { + --ansi-red: #ff5555; + --ansi-green: #50fa7b; + --ansi-blue: #265285; + --ansi-yellow: #ffb86c; /* Orange */ + --ansi-magenta: #bd93f9; /* Purple */ + --ansi-cyan: #8be9fd; + --ansi-black: #282a36; + --ansi-white: #f8f8f2; +} + +.-Color-Green, +.-Color-Faint-Green, +.-Color-Bold-Green { + color: var(--ansi-green); +} +.-Color-Red, +.-Color-Faint-Red, +.-Color-Bold-Red { + color: var(--ansi-red); +} +.-Color-Yellow, +.-Color-Faint-Yellow, +.-Color-Bold-Yellow { + color: var(--ansi-yellow); +} +.-Color-Blue, +.-Color-Faint-Blue, +.-Color-Bold-Blue { + color: var(--ansi-blue); +} +.-Color-Magenta, +.-Color-Faint-Magenta, +.-Color-Bold-Magenta { + color: var(--ansi-magenta); +} +.-Color-Cyan, +.-Color-Faint-Cyan, +.-Color-Bold-Cyan { + color: var(--ansi-cyan); +} +.-Color-White, +.-Color-Faint-White, +.-Color-Bold-White { + color: var(--ansi-white); +} +.-Color-Black, +.-Color-Faint-Black, +.-Color-Bold-Black { + color: var(--ansi-black); +} + +.-Color-Faint { + opacity: 0.5; +} + +.-Color-Bold { + font-weight: bold; +} + +.-Color-Black-BGBlack, +.-Color-BGBlack, +.-Color-Red-BGBlack, +.-Color-Green-BGBlack, +.-Color-Yellow-BGBlack, +.-Color-Blue-BGBlack, +.-Color-Magenta-BGBlack, +.-Color-Cyan-BGBlack, +.-Color-White-BGBlack { + background-color: var(--ansi-black); +} + +.-Color-Black-BGRed, +.-Color-BGRed, +.-Color-Red-BGRed, +.-Color-Green-BGRed, +.-Color-Yellow-BGRed, +.-Color-Blue-BGRed, +.-Color-Magenta-BGRed, +.-Color-Cyan-BGRed, +.-Color-White-BGRed { + background-color: var(--ansi-red); +} + +.-Color-Black-BGGreen, +.-Color-BGGreen, +.-Color-Red-BGGreen, +.-Color-Green-BGGreen, +.-Color-Yellow-BGGreen, +.-Color-Blue-BGGreen, +.-Color-Magenta-BGGreen, +.-Color-Cyan-BGGreen, +.-Color-White-BGGreen { + background-color: var(--ansi-green); +} + +.-Color-Black-BGYellow, +.-Color-BGYellow, +.-Color-Red-BGYellow, +.-Color-Green-BGYellow, +.-Color-Yellow-BGYellow, +.-Color-Blue-BGYellow, +.-Color-Magenta-BGYellow, +.-Color-Cyan-BGYellow, +.-Color-White-BGYellow { + background-color: var(--ansi-yellow); +} + +.-Color-Black-BGBlue, +.-Color-BGBlue, +.-Color-Red-BGBlue, +.-Color-Green-BGBlue, +.-Color-Yellow-BGBlue, +.-Color-Blue-BGBlue, +.-Color-Magenta-BGBlue, +.-Color-Cyan-BGBlue, +.-Color-White-BGBlue { + background-color: var(--ansi-blue); +} + +.-Color-Black-BGMagenta, +.-Color-BGMagenta, +.-Color-Red-BGMagenta, +.-Color-Green-BGMagenta, +.-Color-Yellow-BGMagenta, +.-Color-Blue-BGMagenta, +.-Color-Magenta-BGMagenta, +.-Color-Cyan-BGMagenta, +.-Color-White-BGMagenta { + background-color: var(--ansi-magenta); +} + +.-Color-Black-BGCyan, +.-Color-BGCyan, +.-Color-Red-BGCyan, +.-Color-Green-BGCyan, +.-Color-Yellow-BGCyan, +.-Color-Blue-BGCyan, +.-Color-Magenta-BGCyan, +.-Color-Cyan-BGCyan, +.-Color-White-BGCyan { + background-color: var(--ansi-cyan); +} + +.-Color-Black-BGWhite, +.-Color-BGWhite, +.-Color-Red-BGWhite, +.-Color-Green-BGWhite, +.-Color-Yellow-BGWhite, +.-Color-Blue-BGWhite, +.-Color-Magenta-BGWhite, +.-Color-Cyan-BGWhite, +.-Color-White-BGWhite { + background-color: var(--ansi-white); +} + +.-Color-Black-BGBlack, +.-Color-Red-BGRed, +.-Color-Blue-BGBlue { + text-shadow: 0 0 1px var(--ansi-white); +} + +.-Color-Green-BGGreen, +.-Color-Yellow-BGYellow, +.-Color-Cyan-BGCyan, +.-Color-White-BGWhite, +.-Color-Magenta-BGMagenta, +.-Color-Cyan-BGGreen, +.-Color-Green-BGCyan { + text-shadow: 0 0 1px var(--ansi-black); +} diff --git a/docs/usage.md b/docs/usage.md index f3cd1f065a..14f4a9ebe2 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,18 +1,12 @@ -(using-lint)= - # Using -```{contents} Topics - -``` - ## Using commands -After you install Ansible-lint, run `ansible-lint --help` to display available commands and their options. +After you install Ansible-lint, run `ansible-lint --help` to display available +commands and their options. -```{command-output} ansible-lint --help - :cwd: .. - :returncode: 0 +```bash exec="1" source="console" +ansible-lint --help ``` ### Command output @@ -22,108 +16,123 @@ Ansible-lint prints output on both `stdout` and `stderr`. - `stdout` displays rule violations. - `stderr` displays logging and free-form messages like statistics. -Most `ansible-lint` examples use pep8 as the output format (`-p`) which is machine parseable. +Most `ansible-lint` examples use pep8 as the output format (`-p`) which is +machine parseable. -Ansible-lint also print errors using their [annotation] format when it detects the `GITHUB_ACTIONS=true` and `GITHUB_WORKFLOW=...` variables. +Ansible-lint also print errors using their [annotation] format when it detects +the `GITHUB_ACTIONS=true` and `GITHUB_WORKFLOW=...` variables. -[annotation]: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message +[annotation]: + https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message ## Caching -For optimal performance, Ansible-lint creates caches with installed or mocked roles, collections, and modules in the `{project_dir}/.cache` folder. -The location of `{project_dir}` is passed with a command line argument, determined by the location of the configuration file, git project top-level directory, or user home directory. +For optimal performance, Ansible-lint creates caches with installed or mocked +roles, collections, and modules in the `{project_dir}/.cache` folder. The +location of `{project_dir}` is passed with a command line argument, determined +by the location of the configuration file, git project top-level directory, or +user home directory. To perform faster re-runs, Ansible-lint does not automatically clean the cache. If required you can do this manually by simply deleting the `.cache` folder. Ansible-lint creates a new cache on the next invocation. -You should add the `.cache` folder to the `.gitignore` file in your git repositories. +You should add the `.cache` folder to the `.gitignore` file in your git +repositories. ## Using progressive mode -For easier adoption, Ansible-lint can alert for rule violations that occur since the last commit. -This allows new code to be merged without any rule violations while allowing content developers to address historical violations at a different pace. +For easier adoption, Ansible-lint can alert for rule violations that occur since +the last commit. This allows new code to be merged without any rule violations +while allowing content developers to address historical violations at a +different pace. -The `--progressive` option runs Ansible-lint twice if rule violations exist in your content. -The second run is performed against a temporary git working copy that contains -the last commit. -Rule violations that exist in the last commit are ignored and Ansible-lint displays only the violations that exist in the new commit. +The `--progressive` option runs Ansible-lint twice if rule violations exist in +your content. The second run is performed against a temporary git working copy +that contains the last commit. Rule violations that exist in the last commit are +ignored and Ansible-lint displays only the violations that exist in the new +commit. ## Linting playbooks and roles -Ansible-lint recommends following the {ref}`collection structure layout ` whether you plan to build a collection or not. +Ansible-lint recommends following the +{ref}`collection structure layout ` whether you plan to +build a collection or not. -Following that layout assures the best integration with all ecosystem tools because it helps those tools better distinguish between random YAML files and files managed by Ansible. -When you call `ansible-lint` without arguments, it uses internal heuristics to determine file types. +Following that layout assures the best integration with all ecosystem tools +because it helps those tools better distinguish between random YAML files and +files managed by Ansible. When you call `ansible-lint` without arguments, it +uses internal heuristics to determine file types. -You can specify the list of **roles** or **playbooks** that you want to lint with the `-p` argument. -For example, to lint `examples/playbooks/play.yml` and `examples/roles/bobbins`, use the following command: +You can specify the list of **roles** or **playbooks** that you want to lint +with the `-p` argument. For example, to lint `examples/playbooks/play.yml` and +`examples/roles/bobbins`, use the following command: -```{command-output} ansible-lint --offline -p examples/playbooks/play.yml examples/roles/bobbins - :cwd: .. - :returncode: 2 - :nostderr: true +```bash exec="1" source="console" +ansible-lint --offline -p examples/playbooks/play.yml examples/roles/bobbins || true ``` ## Running example playbooks -Ansible-lint includes an `ansible-lint/examples` folder that contains example playbooks with different rule violations and undesirable characteristics. -You can run `ansible-lint` on the example playbooks to observe Ansible-lint in action, as follows: +Ansible-lint includes an `ansible-lint/examples` folder that contains example +playbooks with different rule violations and undesirable characteristics. You +can run `ansible-lint` on the example playbooks to observe Ansible-lint in +action, as follows: -```{command-output} ansible-lint --offline -p examples/playbooks/example.yml - :cwd: .. - :returncode: 2 - :nostderr: true +```bash exec="1" source="console" +ansible-lint --offline -p examples/playbooks/example.yml >/dev/null || true ``` -Ansible-lint also handles playbooks that include other playbooks, tasks, handlers, or roles, as the `examples/playbooks/include.yml` example demonstrates. +Ansible-lint also handles playbooks that include other playbooks, tasks, +handlers, or roles, as the `examples/playbooks/include.yml` example +demonstrates. -```{command-output} ansible-lint --offline --force-color --offline -p examples/playbooks/include.yml - :cwd: .. - :returncode: 2 - :nostderr: true +```bash exec="1" source="console" +ansible-lint --offline -p examples/playbooks/include.yml 2>/dev/null || true ``` -You can generate `JSON` reports based on the code-climate specification as the `examples/playbooks/norole.yml` example demonstrates. +You can generate `JSON` reports based on the code-climate specification as the +`examples/playbooks/norole.yml` example demonstrates. -```{command-output} ansible-lint --offline -f json examples/playbooks/norole.yml - :cwd: .. - :returncode: 2 - :nostderr: true +```bash exec="1" source="tabbed-left" result="json" +ansible-lint --offline -f json examples/playbooks/norole.yml 2>/dev/null || true ``` ## Specifying rules at runtime -By default, `ansible-lint` applies rules found in `ansible-lint/src/ansiblelint/rules`. -Use the `-r /path/to/custom-rules` option to specify the directory path to a set of custom rules. -For multiple custom rule sets, pass each set with a separate `-r` option. +By default, `ansible-lint` applies rules found in +`ansible-lint/src/ansiblelint/rules`. Use the `-r /path/to/custom-rules` option +to specify the directory path to a set of custom rules. For multiple custom rule +sets, pass each set with a separate `-r` option. -You can also combine the default rules with custom rules with the `-R` option along with one or more `-r` options. +You can also combine the default rules with custom rules with the `-R` option +along with one or more `-r` options. ### Including rules with tags -Each rule has an associated set of one or more tags. -Use the `-T` option to view the list of tags for each available rule. +Each rule has an associated set of one or more tags. Use the `-T` option to view +the list of tags for each available rule. -You can then use the `-t` option to specify a tag and include the associated rules in the lint run. -For example, the following `ansible-lint` command applies only the rules associated with the _idempotency_ tag: +You can then use the `-t` option to specify a tag and include the associated +rules in the lint run. For example, the following `ansible-lint` command applies +only the rules associated with the _idempotency_ tag: -```bash -$ ansible-lint -t idempotency playbook.yml +```bash exec="1" source="console" +ansible-lint -t idempotency playbook.yml ``` -The following shows the available tags in an example set of rules and the rules associated with each tag: +The following shows the available tags in an example set of rules and the rules +associated with each tag: -```{command-output} ansible-lint -T - :cwd: .. - :returncode: 0 - :nostderr: true +```bash exec="1" source="console" +ansible-lint -T 2>/dev/null ``` ### Excluding rules with tags -To exclude rules by identifiers or tags, use the `-x SKIP_LIST` option. -For example, the following command applies all rules except those with the _formatting_ and _metadata_ tags: +To exclude rules by identifiers or tags, use the `-x SKIP_LIST` option. For +example, the following command applies all rules except those with the +_formatting_ and _metadata_ tags: ```bash $ ansible-lint -x formatting,metadata playbook.yml @@ -131,27 +140,31 @@ $ ansible-lint -x formatting,metadata playbook.yml ### Ignoring rules -To only warn about rules, use the `-w WARN_LIST` option. -For example, the following command displays only warns about violations with rules associated with the `experimental` tag: +To only warn about rules, use the `-w WARN_LIST` option. For example, the +following command displays only warns about violations with rules associated +with the `experimental` tag: ```console $ ansible-lint -w experimental playbook.yml ``` -By default, the `WARN_LIST` includes the `['experimental']` tag. -If you define a custom `WARN_LIST` you must add `'experimental'` so that Ansible-lint does not fail against experimental rules. +By default, the `WARN_LIST` includes the `['experimental']` tag. If you define a +custom `WARN_LIST` you must add `'experimental'` so that Ansible-lint does not +fail against experimental rules. ## Muting warnings to avoid false positives -Not all linting rules are precise, some are general rules of thumb. -Advanced _git_, _yum_ or _apt_ usage, for example, can be difficult to achieve in a playbook. -In cases like this, Ansible-lint can incorrectly trigger rule violations. +Not all linting rules are precise, some are general rules of thumb. Advanced +_git_, _yum_ or _apt_ usage, for example, can be difficult to achieve in a +playbook. In cases like this, Ansible-lint can incorrectly trigger rule +violations. -To disable rule violations for specific tasks, and mute false positives, add `# noqa [rule_id]` to the end of the line. -It is best practice to add a comment that explains why rules are disabled. +To disable rule violations for specific tasks, and mute false positives, add +`# noqa [rule_id]` to the end of the line. It is best practice to add a comment +that explains why rules are disabled. -You can add the `# noqa [rule_id]` comment to the end of any line in a task. -You can also skip multiple rules with a space-separated list. +You can add the `# noqa [rule_id]` comment to the end of any line in a task. You +can also skip multiple rules with a space-separated list. ```yaml - name: This task would typically fire git-latest and partial-become rules @@ -168,10 +181,13 @@ If the rule is line-based, `# noqa [rule_id]` must be at the end of the line. dest: "{{dest_proj_path}}/foo.conf" # noqa jinja[spacing] ``` -If you want Ansible-lint to skip a rule entirely, use the `-x` command line argument or add it to `skip_list` in your configuration. +If you want Ansible-lint to skip a rule entirely, use the `-x` command line +argument or add it to `skip_list` in your configuration. -The least preferred method of skipping rules is to skip all task-based rules for a task, which does not skip line-based rules. -You can use the `skip_ansible_lint` tag with all tasks or the `warn` parameter with the _command_ or _shell_ modules, for example: +The least preferred method of skipping rules is to skip all task-based rules for +a task, which does not skip line-based rules. You can use the +`skip_ansible_lint` tag with all tasks or the `warn` parameter with the +_command_ or _shell_ modules, for example: ```yaml - name: This would typically fire deprecated-command-syntax @@ -187,3 +203,47 @@ You can use the `skip_ansible_lint` tag with all tasks or the `warn` parameter w tags: - skip_ansible_lint ``` + +## Applying profiles + +Ansible-lint profiles allow content creators to progressively improve the +quality of Ansible playbooks, roles, and collections. + +During early development cycles, you need Ansible-lint rules to be less strict. +Starting with the minimal profile ensures that Ansible can load your content. As +you move to the next stage of developing content, you can gradually apply +profiles to avoid common pitfalls and brittle complexity. Then, when you are +ready to publish or share your content, you can use the `shared` and +`production` profiles with much stricter rules. These profiles harden security, +guarantee reliability, and ensure your Ansible content is easy for others to +contribute to and use. + +!!! note + + Tags such as `opt-in` and `experimental` do not take effect for rules that are included in profiles, directly or indirectly. + If a rule is in a profile, Ansible-lint applies that rule to the content. + +After you install and configure `ansible-lint`, you can apply profiles as +follows: + +1. View available profiles with the `--list-profiles` flag. + + ```bash + ansible-lint --list-profiles + ``` + +2. Specify a profile with the `--profile` parameter to lint your content with + those rules, for example: + +- Enforce standard styles and formatting with the `basic` profile. + + ```bash + ansible-lint --profile=basic + ``` + +- Ensure automation consistency, reliability, and security with the `safety` + profile. + + ```bash + ansible-lint --profile=safety + ``` diff --git a/docs/using-profiles.md b/docs/using-profiles.md deleted file mode 100644 index 396cae5b34..0000000000 --- a/docs/using-profiles.md +++ /dev/null @@ -1,44 +0,0 @@ -(using-lint-profiles)= - -# Applying profiles - -Ansible-lint profiles allow content creators to progressively improve the quality of Ansible playbooks, roles, and collections. - -During early development cycles, you need Ansible-lint rules to be less strict. -Starting with the minimal profile ensures that Ansible can load your content. -As you move to the next stage of developing content, you can gradually apply profiles to avoid common pitfalls and brittle complexity. -Then, when you are ready to publish or share your content, you can use the `shared` and `production` profiles with much stricter rules. -These profiles harden security, guarantee reliability, and ensure your Ansible content is easy for others to contribute to and use. - -```{note} -Tags such as `opt-in` and `experimental` do not take effect for rules that are included in profiles, directly or indirectly. -If a rule is in a profile, Ansible-lint applies that rule to the content. -``` - -After you install and configure `ansible-lint`, you can apply profiles as follows: - -1. View available profiles with the `--list-profiles` flag. - - ```bash - ansible-lint --list-profiles - ``` - -2. Specify a profile with the `--profile` parameter to lint your content with those rules, for example: - -- Enforce standard styles and formatting with the `basic` profile. - - ```bash - ansible-lint --profile=basic - ``` - -- Ensure automation consistency, reliability, and security with the `safety` profile. - - ```bash - ansible-lint --profile=safety - ``` - -```{toctree} -:maxdepth: 1 - -profiles -``` diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..d8a99ee56b --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,195 @@ +--- +site_name: Ansible List Documentation +site_url: https://ansible-lint.readthedocs.io/ +repo_url: https://github.com/ansible/ansible-lint +edit_uri: blob/main/docs/ +copyright: Copyright © 2023 Red Hat, Inc. +docs_dir: docs +strict: true + +extra_css: + - stylesheets/extra.css + +theme: + name: "material" + logo: images/logo.svg + favicon: images/favicon.ico + features: + - content.code.copy + - content.action.edit + - navigation.expand + - navigation.sections + - navigation.instant + - navigation.indexes + - navigation.tracking + - toc.integrate + palette: + - media: "(prefers-color-scheme: light)" + primary: teal + accent: blue + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: teal + accent: blue + toggle: + icon: material/brightness-4 + name: Switch to light mode +extra: + social: + - icon: fontawesome/brands/python + link: https://pypi.org/project/ansible-lint/ + name: PyPI + - icon: fontawesome/solid/scroll + link: https://github.com/ansible/ansible-lint/releases + name: Releases + - icon: simple/mastodon + link: https://fosstodon.org/@ansible + name: Mastodon + - icon: fontawesome/brands/twitter + link: https://twitter.com/ansible + name: Twitter + - icon: simple/matrix + link: https://matrix.to/#/#devtools:ansible.com + name: Matrix + - icon: fontawesome/solid/comments + link: https://github.com/ansible/ansible-lint/discussions + name: Discussions + - icon: fontawesome/brands/github-alt + link: https://github.com/ansible/ansible-lint + name: GitHub + +nav: + - User Guide: + - home: index.md + - philosophy.md + - installing.md + - usage.md + - configuring.md + - profiles.md + - Rules: + - index: rules/index.md + - rules/args.md + - rules/avoid-implicit.md + - rules/command-instead-of-module.md + - rules/command-instead-of-shell.md + - rules/deprecated-bare-vars.md + - rules/deprecated-command-syntax.md + - rules/deprecated-local-action.md + - rules/deprecated-module.md + - rules/empty-string-compare.md + - rules/fqcn.md + - rules/galaxy.md + - rules/ignore-errors.md + - rules/inline-env-var.md + - rules/internal-error.md + - rules/jinja.md + - rules/key-order.md + - rules/latest.md + - rules/literal-compare.md + - rules/load-failure.md + - rules/loop-var-prefix.md + - rules/meta-incorrect.md + - rules/meta-no-info.md + - rules/meta-no-tags.md + - rules/meta-unsupported-ansible.md + - rules/meta-video-links.md + - rules/name.md + - rules/no-changed-when.md + - rules/no-free-form.md + - rules/no-handler.md + - rules/no-jinja-when.md + - rules/no-log-password.md + - rules/no-prompting.md + - rules/no-relative-paths.md + - rules/no-same-owner.md + - rules/no-tabs.md + - rules/only-builtins.md + - rules/package-latest.md + - rules/parser-error.md + - rules/partial-become.md + - rules/playbook-extension.md + - rules/risky-file-permissions.md + - rules/risky-octal.md + - rules/risky-shell-pipe.md + - rules/role-name.md + - rules/run-once.md + - rules/schema.md + - rules/syntax-check.md + - rules/var-naming.md + - rules/warning.md + - rules/yaml.md + - Developer Guide: + - Contributing: contributing.md + - custom-rules.md + +# - installation.md +# - using: +# - getting-started.md +# - usage.md +# - configuration.md +# - examples.md +# - faq.md +# - contributing.md +# - tags.md +# # - subprocess-tee: '!import https://github.com/pycontribs/subprocess-tee?branch=main&multi_docs=False' +# # - xxx: '!import https://github.com/eclipse-opensmartclide/smartclide-docs' + +plugins: + - autorefs + - markdown-exec + - gen-files: + scripts: + - src/ansiblelint/generate_docs.py + - search + - social + - tags + - mkdocstrings: + handlers: + python: + paths: [src] + options: + # Sphinx is for historical reasons, but we could consider switching if needed + # https://mkdocstrings.github.io/griffe/docstrings/ + docstring_style: sphinx + merge_init_into_class: yes + show_submodules: yes + import: + - url: https://docs.ansible.com/ansible/latest/objects.inv + domains: [py, std] + +markdown_extensions: + - admonition + - def_list + - footnotes + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets: + check_paths: true + - pymdownx.superfences + - pymdownx.magiclink: + repo_url_shortener: true + repo_url_shorthand: true + social_url_shorthand: true + social_url_shortener: true + user: facelessuser + repo: pymdown-extensions + normalize_issue_symbols: true + - pymdownx.tabbed: + alternate_style: true + - toc: + toc_depth: 2 + permalink: true + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - name: python + class: python + validator: !!python/name:markdown_exec.validator + format: !!python/name:markdown_exec.formatter diff --git a/pyproject.toml b/pyproject.toml index a70bf1f009..81080f5553 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,7 @@ show_missing = true profile = "black" # add_imports = "from __future__ import annotations" known_first_party = "ansiblelint" -known_third_party = "ansible,pytest,ruamel,setuptools,sphinx,yaml" +known_third_party = "ansible,pytest,ruamel,setuptools,yaml" # https://black.readthedocs.io/en/stable/the_black_code_style.html#line-length multi_line_output = 3 include_trailing_comma = true diff --git a/src/ansiblelint/_internal/load-failure.md b/src/ansiblelint/_internal/load-failure.md new file mode 100644 index 0000000000..78daa0dcd5 --- /dev/null +++ b/src/ansiblelint/_internal/load-failure.md @@ -0,0 +1,12 @@ +# load-failure + +"Linter failed to process a file, possible invalid file. Possible reasons: + +* contains unsupported encoding (only UTF-8 is supported) +* not an Ansible file +* it contains some unsupported custom YAML objects (`!!` prefix) +* it was not able to decrypt an inline `!vault` block. + +This violation **is not** skippable, so it cannot be added to the `warn_list` +or the `skip_list`. If a vault decryption issue cannot be avoided, the +offending file can be added to `exclude_paths` configuration. diff --git a/src/ansiblelint/_internal/parser-error.md b/src/ansiblelint/_internal/parser-error.md new file mode 100644 index 0000000000..f6c7649286 --- /dev/null +++ b/src/ansiblelint/_internal/parser-error.md @@ -0,0 +1,5 @@ +# parser-error + +**AnsibleParserError.** + +Ansible parser fails; this usually indicates an invalid file. diff --git a/src/ansiblelint/generate_docs.py b/src/ansiblelint/generate_docs.py index 132e33c437..a52e125c69 100644 --- a/src/ansiblelint/generate_docs.py +++ b/src/ansiblelint/generate_docs.py @@ -64,6 +64,7 @@ def rules_as_docs(rules: RulesCollection) -> str: description += f" [more]({rule.link})" result += f"# {title}\n\n**{rule.shortdesc}**\n\n{description}" + result = result.strip() + "\n" f.write(result) return "All markdown files for rules were dumped!" @@ -134,9 +135,9 @@ def profiles_as_md(header: bool = False, docs_url: str = RULE_DOC_URL) -> str: Ansible-lint profiles gradually increase the strictness of rules as your Ansible content lifecycle. -```{note} -Rules with `*` in the suffix are not yet implemented but are documented with linked GitHub issues. -``` +!!! note + + Rules with `*` in the suffix are not yet implemented but are documented with linked GitHub issues. """ diff --git a/src/ansiblelint/rules/fqcn.md b/src/ansiblelint/rules/fqcn.md index 68358b35d3..fc96ed227b 100644 --- a/src/ansiblelint/rules/fqcn.md +++ b/src/ansiblelint/rules/fqcn.md @@ -3,40 +3,43 @@ This rule checks for fully-qualified collection names (FQCN) in Ansible content. Declaring an FQCN ensures that an action uses code from the correct namespace. -This avoids ambiguity and conflicts that can cause operations to fail or produce unexpected results. +This avoids ambiguity and conflicts that can cause operations to fail or produce +unexpected results. The `fqcn` rule has the following checks: - `fqcn[action]` - Use FQCN for module actions, such ... -- `fqcn[action-core]` - Checks for FQCNs from the `ansible.legacy` or `ansible.builtin` collection. +- `fqcn[action-core]` - Checks for FQCNs from the `ansible.legacy` or + `ansible.builtin` collection. - `fqcn[canonical]` - You should use canonical module name ... instead of ... -- `fqcn[keyword]` - Avoid `collections` keyword by using FQCN for all plugins, modules, roles and playbooks. +- `fqcn[keyword]` - Avoid `collections` keyword by using FQCN for all plugins, + modules, roles and playbooks. -```{note} -In most cases you should declare the `ansible.builtin` collection for internal Ansible actions. -You should declare the `ansible.legacy` collection if you use local overrides with actions, such with as the ``shell`` module. -``` +!!! note -```{warning} -This rule does not take [`collections` keyword](https://docs.ansible.com/ansible/latest/collections_guide/collections_using_playbooks.html#simplifying-module-names-with-the-collections-keyword) into consideration for resolving content. -The `collections` keyword provided a temporary mechanism transitioning to Ansible 2.9. -You should rewrite any content that uses the `collections:` key and avoid it where possible. -``` + In most cases you should declare the `ansible.builtin` collection for internal Ansible actions. + You should declare the `ansible.legacy` collection if you use local overrides with actions, such with as the ``shell`` module. + +!!! warning + + This rule does not take [`collections` keyword](https://docs.ansible.com/ansible/latest/collections_guide/collections_using_playbooks.html#simplifying-module-names-with-the-collections-keyword) into consideration for resolving content. + The `collections` keyword provided a temporary mechanism transitioning to Ansible 2.9. + You should rewrite any content that uses the `collections:` key and avoid it where possible. ## Canonical module names -Canonical module names are also known as **resolved module names** and they -are to be preferred for most cases. Many Ansible modules have multiple aliases -and redirects, as these were created over time while the content was refactored. +Canonical module names are also known as **resolved module names** and they are +to be preferred for most cases. Many Ansible modules have multiple aliases and +redirects, as these were created over time while the content was refactored. Still, all of them do finally resolve to the same module name, but not without adding some performance overhead. As very old aliases are at some point removed, it makes to just refresh the content to make it point to the current canonical name. -The only exception for using a canonical name is if your code still needs to -be compatible with a very old version of Ansible, one that does not know how -to resolve that name. If you find yourself in such a situation, feel free to -add this rule to the ignored list. +The only exception for using a canonical name is if your code still needs to be +compatible with a very old version of Ansible, one that does not know how to +resolve that name. If you find yourself in such a situation, feel free to add +this rule to the ignored list. ## Problematic Code @@ -58,7 +61,8 @@ add this rule to the ignored list. tasks: - name: Create an SSH connection # Use the FQCN for the legacy shell module and allow local overrides. - ansible.legacy.shell: ssh ssh_user@{{ ansible_ssh_host }} -o IdentityFile=path/to/my_rsa + ansible.legacy.shell: + ssh ssh_user@{{ ansible_ssh_host }} -o IdentityFile=path/to/my_rsa ``` ```yaml diff --git a/src/ansiblelint/rules/name.md b/src/ansiblelint/rules/name.md index 608ce67687..9df4213415 100644 --- a/src/ansiblelint/rules/name.md +++ b/src/ansiblelint/rules/name.md @@ -6,40 +6,38 @@ This is important because these names are the primary way to **identify** and This rule can produce messages as: -- `name[casing]` - All names should start with an uppercase letter for - languages that support it. +- `name[casing]` - All names should start with an uppercase letter for languages + that support it. - `name[missing]` - All tasks should be named. - `name[play]` - All plays should be named. - `name[prefix]` - Prefix task names in sub-tasks files. (opt-in) - `name[template]` - Jinja templates should only be at the end of 'name'. This helps with the identification of tasks inside the source code when they fail. - The use of templating inside `name` keys is discouraged as there - are multiple cases where the rendering of the name template is not possible. + The use of templating inside `name` keys is discouraged as there are multiple + cases where the rendering of the name template is not possible. -If you want to ignore some of the messages above, you can add any of them to -the `skip_list`. +If you want to ignore some of the messages above, you can add any of them to the +`skip_list`. ## name[prefix] -This rule applies only to included task files that are not named -`main.yml`. It suggests adding the stem of the file as a prefix to the task -name. +This rule applies only to included task files that are not named `main.yml`. It +suggests adding the stem of the file as a prefix to the task name. For example, if you have a task named `Restart server` inside a file named -`tasks/deploy.yml`, this rule suggests renaming it to -`deploy | Restart server`, so it would be easier to identify where it comes -from. +`tasks/deploy.yml`, this rule suggests renaming it to `deploy | Restart server`, +so it would be easier to identify where it comes from. For the moment, this sub-rule is just an **opt-in**, so you need to add it to your `enable_list` to activate it. -```{note} -This rule was designed by [Red Hat Community of Practice](https://redhat-cop.github.io/automation-good-practices/#_prefix_task_names_in_sub_tasks_files_of_roles). The reasoning behind it being -that in a complex roles or playbooks with multiple (sub-)tasks file, it becomes -difficult to understand which task belongs to which file. Adding a prefix, in -combination with the role’s name automatically added by Ansible, makes it a -lot easier to follow and troubleshoot a role play. -``` +!!! note + + This rule was designed by [Red Hat Community of Practice](https://redhat-cop.github.io/automation-good-practices/#_prefix_task_names_in_sub_tasks_files_of_roles). The reasoning behind it being + that in a complex roles or playbooks with multiple (sub-)tasks file, it becomes + difficult to understand which task belongs to which file. Adding a prefix, in + combination with the role’s name automatically added by Ansible, makes it a + lot easier to follow and troubleshoot a role play. ## Problematic code diff --git a/src/ansiblelint/rules/no_free_form.md b/src/ansiblelint/rules/no_free_form.md index c6f5486687..b5317c15fe 100644 --- a/src/ansiblelint/rules/no_free_form.md +++ b/src/ansiblelint/rules/no_free_form.md @@ -1,22 +1,23 @@ # no-free-form -This rule identifies any use of [free-form](https://docs.ansible.com/ansible/2.7/user_guide/playbooks_intro.html#action-shorthand) +This rule identifies any use of +[free-form](https://docs.ansible.com/ansible/2.7/user_guide/playbooks_intro.html#action-shorthand) module calling syntax and asks for switching to the full syntax. **Free-form** syntax, also known as **inline** or **shorthand**, can produce subtle bugs. It can also prevent editors and IDEs from providing feedback, autocomplete and validation for the edited line. -```{note} -As long you just pass a YAML string that contains a `=` character inside as the -parameter to the action module name, we consider this as using free-formsyntax. -Be sure you pass a dictionary to the module, so the free-form parsing -is never triggered. -``` +!!! note + + As long you just pass a YAML string that contains a `=` character inside as the + parameter to the action module name, we consider this as using free-formsyntax. + Be sure you pass a dictionary to the module, so the free-form parsing is never + triggered. As `raw` module only accepts free-form, we trigger `no-free-form[raw]` only if -we detect the presence of `executable=` inside raw calls. We advice the -explicit use of `args:` dictionary for configuring the executable to be run. +we detect the presence of `executable=` inside raw calls. We advice the explicit +use of `args:` dictionary for configuring the executable to be run. ## Problematic code diff --git a/src/ansiblelint/rules/no_relative_paths.md b/src/ansiblelint/rules/no_relative_paths.md index a22a9c1552..568a1455aa 100644 --- a/src/ansiblelint/rules/no_relative_paths.md +++ b/src/ansiblelint/rules/no_relative_paths.md @@ -1,27 +1,35 @@ # no-relative-paths -This rule checks for relative paths in the `ansible.builtin.copy` and `ansible.builtin.template` modules. +This rule checks for relative paths in the `ansible.builtin.copy` and +`ansible.builtin.template` modules. -Relative paths in a task most often direct Ansible to remote files and directories on managed nodes. -In the `ansible.builtin.copy` and `ansible.builtin.template` modules, the `src` argument refers to local files and directories on the control node. +Relative paths in a task most often direct Ansible to remote files and +directories on managed nodes. In the `ansible.builtin.copy` and +`ansible.builtin.template` modules, the `src` argument refers to local files and +directories on the control node. The recommended locations to store files are as follows: -- Use the `files/` folder in the playbook or role directory for the `copy` module. -- Use the `templates/` folder in the playbook or role directory for the `template` module. +- Use the `files/` folder in the playbook or role directory for the `copy` + module. +- Use the `templates/` folder in the playbook or role directory for the + `template` module. -These folders allow you to omit the path or use a sub-folder when specifying files with the `src` argument. +These folders allow you to omit the path or use a sub-folder when specifying +files with the `src` argument. -```{note} -If resources are outside your Ansible playbook or role directory you should use an absolute path with the `src` argument. -``` +!!! note -```{warning} -Do not store resources at the same directory level as your Ansible playbook or tasks files. -Doing this can result in disorganized projects and cause user confusion when distinguishing between resources of the same type, such as YAML. -``` + If resources are outside your Ansible playbook or role directory you should use an absolute path with the `src` argument. + +!!! warning + + Do not store resources at the same directory level as your Ansible playbook or tasks files. + Doing this can result in disorganized projects and cause user confusion when distinguishing between resources of the same type, such as YAML. -See [task paths](https://docs.ansible.com/ansible/latest/playbook_guide/playbook_pathing.html#task-paths) in the Ansible documentation for more information. +See +[task paths](https://docs.ansible.com/ansible/latest/playbook_guide/playbook_pathing.html#task-paths) +in the Ansible documentation for more information. ## Problematic Code diff --git a/src/ansiblelint/rules/no_tabs.md b/src/ansiblelint/rules/no_tabs.md index 53a61d9125..7895122cc7 100644 --- a/src/ansiblelint/rules/no_tabs.md +++ b/src/ansiblelint/rules/no_tabs.md @@ -1,12 +1,12 @@ # no-tabs -This rule checks for the tab character. -The `\t` tab character can result in unexpected display or formatting issues. -You should always use spaces instead of tabs. +This rule checks for the tab character. The `\t` tab character can result in +unexpected display or formatting issues. You should always use spaces instead of +tabs. -```{note} -This rule does not trigger alerts for tab characters in the ``ansible.builtin.lineinfile`` module. -``` +!!! note + + This rule does not trigger alerts for tab characters in the ``ansible.builtin.lineinfile`` module. ## Problematic Code diff --git a/src/ansiblelint/rules/partial_become.md b/src/ansiblelint/rules/partial_become.md index 1b328fe014..01f9dae546 100644 --- a/src/ansiblelint/rules/partial_become.md +++ b/src/ansiblelint/rules/partial_become.md @@ -2,15 +2,16 @@ This rule checks that privilege escalation is activated when changing users. -To perform an action as a different user with the `become_user` directive, you must set `become: true`. - -```{warning} -While Ansible inherits have of `become` and `become_user` from upper levels, -like play level or command line, we do not look at these values. This rule -requires you to be explicit and always define both in the same place, mainly -in order to prevent accidents when some tasks are moved from one location to -another one. -``` +To perform an action as a different user with the `become_user` directive, you +must set `become: true`. + +!!! warning + + While Ansible inherits have of `become` and `become_user` from upper levels, + like play level or command line, we do not look at these values. This rule + requires you to be explicit and always define both in the same place, mainly + in order to prevent accidents when some tasks are moved from one location to + another one. ## Problematic Code diff --git a/src/ansiblelint/rules/risky_file_permissions.md b/src/ansiblelint/rules/risky_file_permissions.md index 30a12f2999..a04e12d3b2 100644 --- a/src/ansiblelint/rules/risky_file_permissions.md +++ b/src/ansiblelint/rules/risky_file_permissions.md @@ -1,16 +1,16 @@ # risky-file-permissions This rule is triggered by various modules that could end up creating new files -on disk with permissions that might be too open, or unpredictable. Please -read the documentation of each module carefully to understand the -implications of using different argument values, as these make the difference -between using the module safely or not. The fix depends on each module and -also your particular situation. +on disk with permissions that might be too open, or unpredictable. Please read +the documentation of each module carefully to understand the implications of +using different argument values, as these make the difference between using the +module safely or not. The fix depends on each module and also your particular +situation. Some modules have a `create` argument that defaults to `true`. For those you -either need to set `create: false` or provide some permissions like -`mode: 0600` to make the behavior predictable and not dependent on the current -system settings. +either need to set `create: false` or provide some permissions like `mode: 0600` +to make the behavior predictable and not dependent on the current system +settings. Modules that are checked: @@ -23,10 +23,10 @@ Modules that are checked: - [`community.general.archive`](https://docs.ansible.com/ansible/latest/collections/community/general/archive_module.html) - [`community.general.ini_file`](https://docs.ansible.com/ansible/latest/collections/community/general/ini_file_module.html) -```{warning} -This rule does not take [module_defaults](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_module_defaults.html) configuration into account. -There are currently no plans to implement this feature because changing task location can also change task behavior. -``` +!!! warning + + This rule does not take [module_defaults](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_module_defaults.html) configuration into account. + There are currently no plans to implement this feature because changing task location can also change task behavior. ## Problematic code diff --git a/src/ansiblelint/rules/run_once.md b/src/ansiblelint/rules/run_once.md index 5621d2939a..6b14703f89 100644 --- a/src/ansiblelint/rules/run_once.md +++ b/src/ansiblelint/rules/run_once.md @@ -5,7 +5,8 @@ This rule warns against the use of `run_once` when `strategy` is set to `free`. This rule can produce the following messages: - `run_once[play]`: Play uses `strategy: free`. -- `run_once[task]`: Using `run_once` may behave differently if `strategy` is set to `free`. +- `run_once[task]`: Using `run_once` may behave differently if `strategy` is set + to `free`. For more information see the following topics in Ansible documentation: @@ -13,11 +14,12 @@ For more information see the following topics in Ansible documentation: - [selecting a strategy](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html#selecting-a-strategy) - [run_once(playbook keyword) more info](https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html) -```{warning} -This rule will always trigger regardless of the value configured inside 'strategy' field. That is because the effective value used at runtime can be different than the value inside the file. For example, ansible command line arguments can alter it. -``` +!!! warning + + This rule will always trigger regardless of the value configured inside 'strategy' field. That is because the effective value used at runtime can be different than the value inside the file. For example, ansible command line arguments can alter it. -It is perfectly fine to add `# noqa: run_once[task]` to mark the warning as acknowledged and ignored. +It is perfectly fine to add `# noqa: run_once[task]` to mark the warning as +acknowledged and ignored. ## Problematic Code @@ -52,7 +54,7 @@ It is perfectly fine to add `# noqa: run_once[task]` to mark the warning as ackn strategy: linear gather_facts: false tasks: # <-- use noqa to disable rule violations for specific tasks - - name: Task with run_once # noqa: run_once[task] + - name: Task with run_once # noqa: run_once[task] ansible.builtin.debug: msg: "Test" run_once: true diff --git a/tox.ini b/tox.ini index bf06627e24..25f3ab825f 100644 --- a/tox.ini +++ b/tox.ini @@ -121,41 +121,16 @@ commands = [testenv:docs] description = Builds docs -deps = - --editable .[docs,test] +extras = + docs +setenv = + # Disable colors until markdown-exec supports it: + # https://github.com/pawamoy/markdown-exec/issues/11 + NO_COLOR = 1 +skip_install = false +usedevelop = true commands = - # regenerate docs for rules - sh -c "cd {toxinidir} && ansible-lint -L -f docs" - sh -c "rm -f {toxinidir}/docs/pkg/*.rst" - {envpython} -m sphinx \ - -j auto \ - -b linkcheck \ - -a \ - -n \ - -W --keep-going \ - {tty:--color} \ - -d "{envdir}/.doctrees" \ - . \ - "{envdir}/html" - - # Build the html docs with Sphinx: - {envpython} -m sphinx \ - -j auto \ - -b html \ - --color \ - -a \ - -n \ - -W --keep-going \ - -d "{envdir}/.doctrees" \ - . \ - "{envdir}/html" - - # Print out the output docs dir and a way to serve html: - - {envpython} -c \ - 'import pathlib, webbrowser; docs_dir = pathlib.Path(r"{envdir}") / "html"; index_file = docs_dir / "index.html"; '\ - 'print(f"\nTo serve docs, use `python3 -m http.server --directory \{docs_dir\} 0`\n"); webbrowser.open(f"file://\{index_file\}")' - -changedir = {toxinidir}/docs + mkdocs build {posargs:} [testenv:redirects] description = Update documentation redirections for readthedocs From 0ae240a24c59cf7e9ce7277b0f4fff2616233d7c Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 30 Jan 2023 17:01:36 +0000 Subject: [PATCH 21/36] Update linters (#2955) --- .pre-commit-config.yaml | 6 +----- .yamllint | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 57006b3a00..00bd471f5a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -107,10 +107,6 @@ repos: )$ additional_dependencies: - tomli - - repo: https://github.com/PyCQA/doc8 - rev: v1.1.1 - hooks: - - id: doc8 - repo: https://github.com/adrienverge/yamllint.git rev: v1.29.0 hooks: @@ -127,7 +123,7 @@ repos: types: [file, yaml] entry: yamllint --strict - repo: https://github.com/PyCQA/isort - rev: 5.11.4 + rev: 5.12.0 hooks: - id: isort args: diff --git a/.yamllint b/.yamllint index 0683b4b7a0..d4971cf025 100644 --- a/.yamllint +++ b/.yamllint @@ -1,5 +1,8 @@ --- rules: + comments: + # prettier compatibility + min-spaces-from-content: 1 document-start: present: true indentation: @@ -12,4 +15,5 @@ ignore: | examples/playbooks/vars/not_decryptable.yml test/schemas/negative_test/**/*.yml test/schemas/test/**/*.yml + # ignore added because this file includes on-purpose errors From b1252558b5497ed7b6130e1b1670a3eba72bd95f Mon Sep 17 00:00:00 2001 From: Todd Lewis Date: Mon, 30 Jan 2023 12:06:31 -0500 Subject: [PATCH 22/36] Fix typo in doc site name (#2954) Co-authored-by: Todd Lewis Co-authored-by: Sorin Sbarnea --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index d8a99ee56b..f4b1f6c5c5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ --- -site_name: Ansible List Documentation +site_name: Ansible Lint Documentation site_url: https://ansible-lint.readthedocs.io/ repo_url: https://github.com/ansible/ansible-lint edit_uri: blob/main/docs/ From 208ce1b2319fbeae89d621e6540036d3c87e1c55 Mon Sep 17 00:00:00 2001 From: Ajinkya Udgirkar Date: Mon, 30 Jan 2023 22:50:16 +0530 Subject: [PATCH 23/36] Correct run-once rule identifier and related examples (#2952) Co-authored-by: Ajinkya Udgirkar Co-authored-by: Sorin Sbarnea --- examples/playbooks/run-once-fail.yml | 4 +++- src/ansiblelint/rules/run_once.md | 16 +++++++++------- src/ansiblelint/rules/run_once.py | 8 ++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/playbooks/run-once-fail.yml b/examples/playbooks/run-once-fail.yml index cb7621812d..f24d52cb4e 100644 --- a/examples/playbooks/run-once-fail.yml +++ b/examples/playbooks/run-once-fail.yml @@ -1,9 +1,11 @@ --- - name: "Example with run_once" hosts: all - strategy: free # <-- avoid use of strategy as free + # strategy: free # noqa: run-once[play] (Corrected code example) + strategy: free gather_facts: false tasks: + # - name: Task with run_once # noqa run-once[task] (Corrected code example) - name: Task with run_once ansible.builtin.debug: msg: "Test" diff --git a/src/ansiblelint/rules/run_once.md b/src/ansiblelint/rules/run_once.md index 6b14703f89..b7df02c499 100644 --- a/src/ansiblelint/rules/run_once.md +++ b/src/ansiblelint/rules/run_once.md @@ -1,12 +1,13 @@ # run-once -This rule warns against the use of `run_once` when `strategy` is set to `free`. +This rule warns against the use of `run_once` when the `strategy` is set to +`free`. This rule can produce the following messages: -- `run_once[play]`: Play uses `strategy: free`. -- `run_once[task]`: Using `run_once` may behave differently if `strategy` is set - to `free`. +- `run-once[play]`: Play uses `strategy: free`. +- `run-once[task]`: Using `run_once` may behave differently if the `strategy` is + set to `free`. For more information see the following topics in Ansible documentation: @@ -16,9 +17,9 @@ For more information see the following topics in Ansible documentation: !!! warning - This rule will always trigger regardless of the value configured inside 'strategy' field. That is because the effective value used at runtime can be different than the value inside the file. For example, ansible command line arguments can alter it. + This rule will always trigger regardless of the value configured inside the 'strategy' field. That is because the effective value used at runtime can be different than the value inside the file. For example, ansible command line arguments can alter it. -It is perfectly fine to add `# noqa: run_once[task]` to mark the warning as +It is perfectly fine to add `# noqa: run-once[task]` to mark the warning as acknowledged and ignored. ## Problematic Code @@ -52,9 +53,10 @@ acknowledged and ignored. - name: "Example of using run_once with strategy other than free" hosts: all strategy: linear + # strategy: free # noqa: run-once[play] (if using strategy: free can skip it this way) gather_facts: false tasks: # <-- use noqa to disable rule violations for specific tasks - - name: Task with run_once # noqa: run_once[task] + - name: Task with run_once # noqa: run-once[task] ansible.builtin.debug: msg: "Test" run_once: true diff --git a/src/ansiblelint/rules/run_once.py b/src/ansiblelint/rules/run_once.py index ea49654bee..2503803353 100644 --- a/src/ansiblelint/rules/run_once.py +++ b/src/ansiblelint/rules/run_once.py @@ -4,6 +4,7 @@ import sys from typing import TYPE_CHECKING, Any +from ansiblelint.constants import LINE_NUMBER_KEY from ansiblelint.errors import MatchError from ansiblelint.rules import AnsibleLintRule @@ -36,7 +37,9 @@ def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]: self.create_matcherror( message="Play uses strategy: free", filename=file, - tag="run_once[play]", + tag=f"{self.id}[play]", + # pylint: disable=protected-access + linenumber=strategy._line_number, ) ] @@ -54,7 +57,8 @@ def matchtask( self.create_matcherror( message="Using run_once may behave differently if strategy is set to free.", filename=file, - tag="run_once[task]", + tag=f"{self.id}[task]", + linenumber=task[LINE_NUMBER_KEY], ) ] From 52a9da620a9296900987e8a42ad472afa7991409 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 17:21:13 +0000 Subject: [PATCH 24/36] Bump mkdocs-material from 9.0.6 to 9.0.7 in /.config (#2950) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.0.6 to 9.0.7. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.0.6...9.0.7) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .config/requirements.txt | 65 +--------------------------------------- 1 file changed, 1 insertion(+), 64 deletions(-) diff --git a/.config/requirements.txt b/.config/requirements.txt index 48237b167e..e6bb42f17b 100644 --- a/.config/requirements.txt +++ b/.config/requirements.txt @@ -5,94 +5,31 @@ # pip-compile --extra=docs --extra=test --no-annotate --output-file=.config/requirements.txt --resolver=backtracking --strip-extras --unsafe-package=ansible-core pyproject.toml # ansible-compat==2.2.7 -astroid==2.13.2 +ansible-core==2.14.1 ; python_version >= "3.9" attrs==22.2.0 black==22.12.0 bracex==2.3.post1 -cairocffi==1.4.0 -cairosvg==2.6.0 -certifi==2022.12.7 cffi==1.15.1 -charset-normalizer==3.0.1 click==8.1.3 -colorama==0.4.6 -coverage==7.0.5 -coverage-enable-subprocess==1.0 cryptography==39.0.0 -cssselect2==0.7.0 -defusedxml==0.7.1 -dill==0.3.6 -exceptiongroup==1.1.0 -execnet==1.9.0 filelock==3.9.0 -flake8==6.0.0 -flake8-future-annotations==1.0.0 -ghp-import==2.1.0 -griffe==0.25.4 -idna==3.4 -importlib-metadata==6.0.0 -iniconfig==2.0.0 -isort==5.11.4 jinja2==3.1.2 jsonschema==4.17.3 -lazy-object-proxy==1.9.0 -markdown==3.3.7 -markdown-exec==1.0.0 markdown-it-py==2.1.0 markupsafe==2.1.1 -mccabe==0.7.0 mdurl==0.1.2 -mergedeep==1.3.4 -mkdocs==1.4.2 -mkdocs-autorefs==0.4.1 -mkdocs-gen-files==0.4.0 -mkdocs-material==9.0.6 -mkdocs-material-extensions==1.1.1 -mkdocstrings==0.20.0 -mkdocstrings-python==0.8.3 -mypy==0.991 mypy-extensions==0.4.3 packaging==23.0 pathspec==0.10.3 -pillow==9.4.0 -pipdeptree==2.3.3 platformdirs==2.6.2 -pluggy==1.0.0 -psutil==5.9.4 -pycodestyle==2.10.0 pycparser==2.21 -pyflakes==3.0.1 pygments==2.14.0 -pylint==2.15.10 -pymdown-extensions==9.9.2 pyrsistent==0.19.3 -pytest==7.2.1 -pytest-mock==3.10.0 -pytest-plus==0.4.0 -pytest-xdist==3.1.0 -python-dateutil==2.8.2 pyyaml==6.0 -pyyaml-env-tag==0.1 -regex==2022.10.31 -requests==2.28.2 resolvelib==0.8.1 rich==13.2.0 ruamel-yaml==0.17.21 -ruamel-yaml-clib==0.2.7 setuptools==66.0.0 -six==1.16.0 subprocess-tee==0.4.1 -tinycss2==1.2.1 -tomli==2.0.1 -tomlkit==0.11.6 -typing-extensions==4.4.0 -urllib3==1.26.14 -watchdog==2.2.1 wcmatch==8.4.1 -webencodings==0.5.1 -wrapt==1.14.1 yamllint==1.29.0 -zipp==3.11.0 - -# The following packages are considered to be unsafe in a requirements file: -# ansible-core From 4b9771ba0d1cea8ee53276f5c1ca4a113c841d4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 17:21:42 +0000 Subject: [PATCH 25/36] Bump markdown-exec from 1.0.0 to 1.1.0 in /.config (#2951) Bumps [markdown-exec](https://github.com/pawamoy/markdown-exec) from 1.0.0 to 1.1.0. - [Release notes](https://github.com/pawamoy/markdown-exec/releases) - [Changelog](https://github.com/pawamoy/markdown-exec/blob/master/CHANGELOG.md) - [Commits](https://github.com/pawamoy/markdown-exec/compare/1.0.0...1.1.0) --- updated-dependencies: - dependency-name: markdown-exec dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 234ebfb8f68c4abd1635e7442e395fc6c609dba5 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 30 Jan 2023 17:30:52 +0000 Subject: [PATCH 26/36] Remove zuul-jobs and tripleo-ansible from eco pipelines (#2948) Removing zuul-jobs and tripleo-ansible as the maintainers of these repositories did not show active interest in updating their codebase based on ansible-lint updates. --- test/test_eco.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_eco.py b/test/test_eco.py index f6864f4f05..8cb45035c0 100644 --- a/test/test_eco.py +++ b/test/test_eco.py @@ -17,8 +17,6 @@ "docker-rootless": "https://github.com/konstruktoid/ansible-docker-rootless", "hardening": "https://github.com/konstruktoid/ansible-role-hardening", "mysql": "https://github.com/geerlingguy/ansible-role-mysql.git", - "tripleo-ansible": "https://opendev.org/openstack/tripleo-ansible", - "zuul-jobs": "https://opendev.org/zuul/zuul-jobs", } From 90eac8a020fbf1d17be7d09ea69c7e1e241572a2 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 30 Jan 2023 17:37:08 +0000 Subject: [PATCH 27/36] Rename warning[raw-non-string] to no-free-form[raw-non-string] (#2956) --- src/ansiblelint/rules/no_free_form.md | 6 ++++++ src/ansiblelint/rules/no_free_form.py | 8 +++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ansiblelint/rules/no_free_form.md b/src/ansiblelint/rules/no_free_form.md index b5317c15fe..3ce514034f 100644 --- a/src/ansiblelint/rules/no_free_form.md +++ b/src/ansiblelint/rules/no_free_form.md @@ -19,6 +19,12 @@ As `raw` module only accepts free-form, we trigger `no-free-form[raw]` only if we detect the presence of `executable=` inside raw calls. We advice the explicit use of `args:` dictionary for configuring the executable to be run. +This rule can produce messages such: + +- `no-free-form` - Free-form syntax is discouraged. +- `no-free-form[raw-non-string]` - Passing a non string value to `raw` module is + neither documented or supported. + ## Problematic code ```yaml diff --git a/src/ansiblelint/rules/no_free_form.py b/src/ansiblelint/rules/no_free_form.py index 1e121f11f4..3aacbe892b 100644 --- a/src/ansiblelint/rules/no_free_form.py +++ b/src/ansiblelint/rules/no_free_form.py @@ -5,7 +5,6 @@ import sys from typing import TYPE_CHECKING, Any -from ansiblelint._internal.rules import WarningRule from ansiblelint.constants import INCLUSION_ACTION_NAMES, LINE_NUMBER_KEY from ansiblelint.errors import MatchError from ansiblelint.rules import AnsibleLintRule @@ -50,12 +49,11 @@ def matchtask( ) else: results.append( - MatchError( - message="Passing a non string value to `raw` module is neither document nor supported.", + self.create_matcherror( + message="Passing a non string value to `raw` module is neither documented or supported.", linenumber=task[LINE_NUMBER_KEY], filename=file, - tag="warning[raw-non-string]", - rule=WarningRule(), + tag=f"{self.id}[raw-non-string]", ) ) elif isinstance(action_value, str) and "=" in action_value: From d04045c52ccaca9ebb806aca3c44b29ba1bab605 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 30 Jan 2023 18:21:33 +0000 Subject: [PATCH 28/36] Update deps (#2957) --- .config/requirements-lock.txt | 3 +- .config/requirements.txt | 67 +++++++++++++++++++++++++++++++++-- .pre-commit-config.yaml | 6 ++-- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/.config/requirements-lock.txt b/.config/requirements-lock.txt index 6e111e0f0c..4e109f5952 100644 --- a/.config/requirements-lock.txt +++ b/.config/requirements-lock.txt @@ -11,12 +11,13 @@ black==22.12.0 bracex==2.3.post1 cffi==1.15.1 click==8.1.3 -commonmark==0.9.1 cryptography==38.0.4 filelock==3.9.0 jinja2==3.1.2 jsonschema==4.17.3 +markdown-it-py==2.1.0 markupsafe==2.1.1 +mdurl==0.1.2 mypy-extensions==0.4.3 packaging==22.0 pathspec==0.10.3 diff --git a/.config/requirements.txt b/.config/requirements.txt index e6bb42f17b..42dda18385 100644 --- a/.config/requirements.txt +++ b/.config/requirements.txt @@ -2,34 +2,97 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile --extra=docs --extra=test --no-annotate --output-file=.config/requirements.txt --resolver=backtracking --strip-extras --unsafe-package=ansible-core pyproject.toml +# pip-compile --extra=docs --extra=test --no-annotate --output-file=.config/requirements.txt --resolver=backtracking --strip-extras --unsafe-package=ansible-core --unsafe-package=ruamel-yaml-clib pyproject.toml # ansible-compat==2.2.7 -ansible-core==2.14.1 ; python_version >= "3.9" +astroid==2.13.3 attrs==22.2.0 black==22.12.0 bracex==2.3.post1 +cairocffi==1.4.0 +cairosvg==2.6.0 +certifi==2022.12.7 cffi==1.15.1 +charset-normalizer==3.0.1 click==8.1.3 +colorama==0.4.6 +coverage==7.1.0 +coverage-enable-subprocess==1.0 cryptography==39.0.0 +cssselect2==0.7.0 +defusedxml==0.7.1 +dill==0.3.6 +exceptiongroup==1.1.0 +execnet==1.9.0 filelock==3.9.0 +flake8==6.0.0 +flake8-future-annotations==1.1.0 +ghp-import==2.1.0 +griffe==0.25.4 +idna==3.4 +importlib-metadata==6.0.0 +iniconfig==2.0.0 +isort==5.12.0 jinja2==3.1.2 jsonschema==4.17.3 +lazy-object-proxy==1.9.0 +markdown==3.3.7 +markdown-exec==1.1.0 markdown-it-py==2.1.0 markupsafe==2.1.1 +mccabe==0.7.0 mdurl==0.1.2 +mergedeep==1.3.4 +mkdocs==1.4.2 +mkdocs-autorefs==0.4.1 +mkdocs-gen-files==0.4.0 +mkdocs-material==9.0.8 +mkdocs-material-extensions==1.1.1 +mkdocstrings==0.20.0 +mkdocstrings-python==0.8.3 +mypy==0.991 mypy-extensions==0.4.3 packaging==23.0 pathspec==0.10.3 +pillow==9.4.0 +pipdeptree==2.3.3 platformdirs==2.6.2 +pluggy==1.0.0 +psutil==5.9.4 +pycodestyle==2.10.0 pycparser==2.21 +pyflakes==3.0.1 pygments==2.14.0 +pylint==2.15.10 +pymdown-extensions==9.9.2 pyrsistent==0.19.3 +pytest==7.2.1 +pytest-mock==3.10.0 +pytest-plus==0.4.0 +pytest-xdist==3.1.0 +python-dateutil==2.8.2 pyyaml==6.0 +pyyaml-env-tag==0.1 +regex==2022.10.31 +requests==2.28.2 resolvelib==0.8.1 rich==13.2.0 ruamel-yaml==0.17.21 setuptools==66.0.0 +six==1.16.0 subprocess-tee==0.4.1 +tinycss2==1.2.1 +tomli==2.0.1 +tomlkit==0.11.6 +typing-extensions==4.4.0 +urllib3==1.26.14 +watchdog==2.2.1 wcmatch==8.4.1 +webencodings==0.5.1 +wrapt==1.14.1 yamllint==1.29.0 +zipp==3.12.0 + +# The following packages are considered to be unsafe in a requirements file: +# ansible-core +# ruamel-yaml-clib diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 00bd471f5a..d6a31510a0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -203,7 +203,7 @@ repos: - id: pip-compile name: lock always_run: true - entry: pip-compile --resolver=backtracking -q --no-annotate --output-file=.config/requirements-lock.txt pyproject.toml --strip-extras --unsafe-package ruamel-yaml-clib + entry: pip-compile --resolver=backtracking -q --no-annotate --output-file=.config/requirements-lock.txt pyproject.toml --strip-extras --unsafe-package ruamel-yaml-clib --unsafe-package ansible-core language: python files: ^.config\/requirements.*$ alias: lock @@ -213,7 +213,7 @@ repos: - pip>=22.3.1 - id: pip-compile name: deps - entry: pip-compile --resolver=backtracking -q --no-annotate --output-file=.config/requirements.txt pyproject.toml --extra docs --extra test --strip-extras --unsafe-package ansible-core + entry: pip-compile --resolver=backtracking -q --no-annotate --output-file=.config/requirements.txt pyproject.toml --extra docs --extra test --strip-extras --unsafe-package ruamel-yaml-clib --unsafe-package ansible-core language: python files: ^.config\/requirements.*$ alias: deps @@ -222,7 +222,7 @@ repos: additional_dependencies: - pip>=22.3.1 - id: pip-compile - entry: pip-compile --resolver=backtracking -q --no-annotate --output-file=.config/requirements.txt pyproject.toml --extra docs --extra test --strip-extras --unsafe-package ansible-core --upgrade + entry: pip-compile --resolver=backtracking -q --no-annotate --output-file=.config/requirements.txt pyproject.toml --extra docs --extra test --strip-extras --unsafe-package ruamel-yaml-clib --unsafe-package ansible-core --upgrade language: python always_run: true files: ^.config\/requirements.*$ From 3650ef0840f93595de7f56859d80ab8c14f9d229 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Tue, 31 Jan 2023 14:27:00 +0000 Subject: [PATCH 29/36] Enable devel testing (#2958) --- .github/workflows/tox.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index fe334a5a1c..8fcf4bac86 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -46,6 +46,7 @@ jobs: pkg,hook,docs schemas eco + py-devel platforms: linux,macos build: From 315d6a94a177c05a23726b6f74aae13a71d0fae6 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 1 Feb 2023 15:02:34 +0000 Subject: [PATCH 30/36] Update schemas (#2967) --- src/ansiblelint/schemas/__store__.json | 2 +- .../schemas/ansible-navigator-config.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ansiblelint/schemas/__store__.json b/src/ansiblelint/schemas/__store__.json index 5d69b30396..489c01faa8 100644 --- a/src/ansiblelint/schemas/__store__.json +++ b/src/ansiblelint/schemas/__store__.json @@ -4,7 +4,7 @@ "url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible-lint-config.json" }, "ansible-navigator-config": { - "etag": "a330d162e7e4b168633c03ba2898c9b499f0e97eccbded6a1467bb94aa4d2ccc", + "etag": "c1038568788867f861cdba2aefde88a71b8ab1a6d874ecfa84eb14c110787677", "url": "https://raw.githubusercontent.com/ansible/ansible-navigator/main/src/ansible_navigator/data/ansible-navigator.json" }, "arg_specs": { diff --git a/src/ansiblelint/schemas/ansible-navigator-config.json b/src/ansiblelint/schemas/ansible-navigator-config.json index eb1e56c302..5acc39c1c4 100644 --- a/src/ansiblelint/schemas/ansible-navigator-config.json +++ b/src/ansiblelint/schemas/ansible-navigator-config.json @@ -237,6 +237,15 @@ }, "type": "object" }, + "enable-prompts": { + "default": false, + "description": "Enable prompts for password and in playbooks. This will set mode to stdout and disable playbook artifact creation", + "enum": [ + true, + false + ], + "type": "boolean" + }, "exec": { "additionalProperties": false, "properties": { @@ -357,6 +366,15 @@ }, "type": "object" }, + "format": { + "default": "yaml", + "description": "Specify the format for stdout output.", + "enum": [ + "json", + "yaml" + ], + "type": "string" + }, "images": { "additionalProperties": false, "properties": { From f79a9bca3eca26512f1a7444730c29f2a9ed91b9 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 1 Feb 2023 15:34:37 +0000 Subject: [PATCH 31/36] Add support for Ansible 2.15 (#2960 This should make the devel testing report green. --- .config/requirements-lock.txt | 2 +- .config/requirements.in | 2 +- .config/requirements.txt | 2 +- .pre-commit-config.yaml | 4 ++-- src/ansiblelint/app.py | 3 ++- test/test_utils.py | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.config/requirements-lock.txt b/.config/requirements-lock.txt index 4e109f5952..5c86c052ea 100644 --- a/.config/requirements-lock.txt +++ b/.config/requirements-lock.txt @@ -4,7 +4,7 @@ # # pip-compile --no-annotate --output-file=.config/requirements-lock.txt --resolver=backtracking --strip-extras --unsafe-package=ruamel-yaml-clib pyproject.toml # -ansible-compat==2.2.7 +ansible-compat==3.0.1 ansible-core==2.14.1 ; python_version >= "3.9" attrs==22.2.0 black==22.12.0 diff --git a/.config/requirements.in b/.config/requirements.in index 94585501b2..3b4fd59c4a 100644 --- a/.config/requirements.in +++ b/.config/requirements.in @@ -2,7 +2,7 @@ will-not-work-on-windows-try-from-wsl-instead; platform_system=='Windows' ansible-core>=2.12.0,<2.14.0; python_version<'3.9' # GPLv3 ansible-core>=2.12.0; python_version>='3.9' # GPLv3 -ansible-compat>=2.2.7 # GPLv3 +ansible-compat>=3.0.1 # GPLv3 # alphabetically sorted: black>=22.8.0 # MIT filelock>=3.3.0 # The Unlicense diff --git a/.config/requirements.txt b/.config/requirements.txt index 42dda18385..189c8fcff1 100644 --- a/.config/requirements.txt +++ b/.config/requirements.txt @@ -4,7 +4,7 @@ # # pip-compile --extra=docs --extra=test --no-annotate --output-file=.config/requirements.txt --resolver=backtracking --strip-extras --unsafe-package=ansible-core --unsafe-package=ruamel-yaml-clib pyproject.toml # -ansible-compat==2.2.7 +ansible-compat==3.0.1 astroid==2.13.3 attrs==22.2.0 black==22.12.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6a31510a0..d11958e393 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -159,7 +159,7 @@ repos: # empty args needed in order to match mypy cli behavior args: [--strict] additional_dependencies: - - ansible-compat>=2.2.6 + - ansible-compat>=3.0.1 - black>=22.10.0 - cryptography - filelock @@ -184,7 +184,7 @@ repos: args: - --output-format=colorized additional_dependencies: - - ansible-compat>=2.2.6 + - ansible-compat>=3.0.1 - ansible-core>=2.14.0 - black>=22.10.0 - docutils diff --git a/src/ansiblelint/app.py b/src/ansiblelint/app.py index 1b49611801..8ce726f21f 100644 --- a/src/ansiblelint/app.py +++ b/src/ansiblelint/app.py @@ -45,7 +45,8 @@ def __init__(self, options: Namespace): formatter_factory = choose_formatter_factory(options) self.formatter = formatter_factory(options.cwd, options.display_relative_path) - self.runtime = Runtime(isolated=True) + # Without require_module, our _set_collections_basedir may fail + self.runtime = Runtime(isolated=True, require_module=True) def render_matches(self, matches: list[MatchError]) -> None: """Display given matches (if they are not fixed).""" diff --git a/test/test_utils.py b/test/test_utils.py index 1b8f9effa2..15f420cc3e 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -40,7 +40,7 @@ from ansiblelint.constants import VIOLATIONS_FOUND_RC from ansiblelint.file_utils import Lintable -runtime = Runtime() +runtime = Runtime(require_module=True) @pytest.mark.parametrize( From ffefdd1e6bd8d53d0917ce53cfb5245c2813cf76 Mon Sep 17 00:00:00 2001 From: Ajinkya Udgirkar Date: Wed, 1 Feb 2023 22:28:13 +0530 Subject: [PATCH 32/36] Improved coverage for empty string compare and galaxy rules (#2963) Co-authored-by: Ajinkya Udgirkar Co-authored-by: Sorin Sbarnea --- .github/workflows/tox.yml | 2 +- examples/no_collection_version/galaxy.yml | 13 +++++++ src/ansiblelint/rules/empty_string_compare.py | 8 ++++ src/ansiblelint/rules/galaxy.py | 38 +++++++++++++++++-- 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 examples/no_collection_version/galaxy.yml diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 8fcf4bac86..078bfb2369 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -70,7 +70,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 771 + PYTEST_REQPASS: 774 steps: - name: Activate WSL1 diff --git a/examples/no_collection_version/galaxy.yml b/examples/no_collection_version/galaxy.yml new file mode 100644 index 0000000000..8f55d91ffe --- /dev/null +++ b/examples/no_collection_version/galaxy.yml @@ -0,0 +1,13 @@ +--- +name: galaxy_no_version +namespace: test +readme: ../README.md +authors: + - John +description: your collection description +license: + - GPL + - Apache + +dependencies: {} +repository: http://example.com/repository diff --git a/src/ansiblelint/rules/empty_string_compare.py b/src/ansiblelint/rules/empty_string_compare.py index e8899f344a..8bc4b5985d 100644 --- a/src/ansiblelint/rules/empty_string_compare.py +++ b/src/ansiblelint/rules/empty_string_compare.py @@ -64,6 +64,11 @@ def matchtask( /sbin/shutdown -t now echo $var == "" when: ansible_os_family + - name: Shut down + shell: | + /sbin/shutdown -t now + echo $var == "" + when: [ansible_os_family] """ FAIL_PLAY = """ @@ -75,6 +80,9 @@ def matchtask( - name: Shut down command: /sbin/shutdown -t now when: ansible_os_family !="" + - name: Shut down + command: /sbin/shutdown -t now + when: False """ @pytest.mark.parametrize( diff --git a/src/ansiblelint/rules/galaxy.py b/src/ansiblelint/rules/galaxy.py index 168ab1a6a8..c29751b6e7 100644 --- a/src/ansiblelint/rules/galaxy.py +++ b/src/ansiblelint/rules/galaxy.py @@ -6,6 +6,8 @@ from functools import total_ordering from typing import TYPE_CHECKING, Any +import pytest + from ansiblelint.constants import LINE_NUMBER_KEY from ansiblelint.errors import MatchError from ansiblelint.rules import AnsibleLintRule @@ -39,6 +41,10 @@ def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]: filename=file, ) ) + # returning here as it does not make sense + # to continue for version check below + return results + version = data.get("version") if Version(version) < Version("1.0.0"): results.append( @@ -88,16 +94,18 @@ def __init__(self, version_string: str): def __eq__(self, other: object) -> bool: """Implement equality comparison.""" - other = _coerce(other) - if not isinstance(other, Version): + try: + other = _coerce(other) + except NotImplementedError: return NotImplemented return self.components == other.components def __lt__(self, other: Version) -> bool: """Implement lower-than operation.""" - other = _coerce(other) - if not isinstance(other, Version): + try: + other = _coerce(other) + except NotImplementedError: return NotImplemented return self.components < other.components @@ -135,6 +143,15 @@ def test_galaxy_collection_version_negative() -> None: errs = bad_runner.run() assert len(errs) == 1 + def test_galaxy_no_collection_version() -> None: + """Test for no collection version in galaxy.""" + collection = RulesCollection() + collection.register(GalaxyRule()) + failure = "examples/no_collection_version/galaxy.yml" + bad_runner = Runner(failure, rules=collection) + errs = bad_runner.run() + assert len(errs) == 1 + def test_changelog_present() -> None: """Positive test for finding a changelog.""" collection = RulesCollection() @@ -151,3 +168,16 @@ def test_changelog_missing() -> None: assert len(result) == 1 for item in result: assert item.tag == "galaxy[no-changelog]" + + def test_version_class() -> None: + """Test for version class.""" + v = Version("1.0.0") + assert v == Version("1.0.0") + + def test_coerce() -> None: + """Test for _coerce function.""" + assert _coerce("1.0") == Version("1.0") + assert _coerce(1.0) == Version("1.0") + expected = "Unable to coerce object type" + with pytest.raises(NotImplementedError, match=expected): + _coerce(type(Version)) From f486b66553a22e2a54a2849fdbc9ca657c907af6 Mon Sep 17 00:00:00 2001 From: Jens Rudolf <58146618+jensrudolf@users.noreply.github.com> Date: Wed, 1 Feb 2023 19:04:40 +0100 Subject: [PATCH 33/36] Do not install collection locally in offline mode (#2926)Fixes https://github.com/ansible/ansible-lint/issues/2922 --- src/ansiblelint/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansiblelint/app.py b/src/ansiblelint/app.py index 8ce726f21f..ad71f42a9a 100644 --- a/src/ansiblelint/app.py +++ b/src/ansiblelint/app.py @@ -353,7 +353,7 @@ def get_app() -> App: # fail. _perform_mockings() app.runtime.prepare_environment( - install_local=True, offline=offline, role_name_check=role_name_check + install_local=(not offline), offline=offline, role_name_check=role_name_check ) return app From f7779053c05a1f3923902bc952af0c4ce44077dc Mon Sep 17 00:00:00 2001 From: Ajinkya Udgirkar Date: Wed, 1 Feb 2023 23:36:54 +0530 Subject: [PATCH 34/36] Added example for skip list on command line (#2964)Co-authored-by: Ajinkya Udgirkar Co-authored-by: Sorin Sbarnea Co-authored-by: Sorin Sbarnea Fixes https://github.com/ansible/ansible-lint/issues/2946 * Added example for skip list on command line * Update cli.py --------- Co-authored-by: Ajinkya Udgirkar Co-authored-by: Sorin Sbarnea Co-authored-by: Sorin Sbarnea --- src/ansiblelint/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ansiblelint/cli.py b/src/ansiblelint/cli.py index f6f565ee6c..cce5471546 100644 --- a/src/ansiblelint/cli.py +++ b/src/ansiblelint/cli.py @@ -364,7 +364,8 @@ def get_cli_parser() -> argparse.ArgumentParser: dest="skip_list", default=[], action="append", - help="only check rules whose id/tags do not match these values", + help="only check rules whose id/tags do not match these values. \ + e.g: --skip-list=name,run-once", ) parser.add_argument( "-w", From 386d63dc21292fa534ab499adb9b3f2a9d009519 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 1 Feb 2023 18:27:53 +0000 Subject: [PATCH 35/36] Change the way rules are listed from command line (#2940 - rename plain formatting to brief - make brief implicit formatting for rule listing - rename rich formatting to full --- .config/dictionary.txt | 1 + src/ansiblelint/__main__.py | 8 +++++--- src/ansiblelint/cli.py | 19 +++++++++++++------ src/ansiblelint/config.py | 2 +- src/ansiblelint/generate_docs.py | 14 ++++++++++++-- test/test_list_rules.py | 6 +++--- test/test_rules_collection.py | 2 +- 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/.config/dictionary.txt b/.config/dictionary.txt index d32df04fa9..9043afd120 100644 --- a/.config/dictionary.txt +++ b/.config/dictionary.txt @@ -19,6 +19,7 @@ PYTHONPYCACHEPREFIX REQPASS RULEDIRS RUNLEVEL +Renderable Representer SRCROOT Sbarnea diff --git a/src/ansiblelint/__main__.py b/src/ansiblelint/__main__.py index e706bdda59..d3f5d350dc 100755 --- a/src/ansiblelint/__main__.py +++ b/src/ansiblelint/__main__.py @@ -141,13 +141,15 @@ def _do_list(rules: RulesCollection) -> int: if options.list_rules: _rule_format_map: dict[str, Callable[..., Any]] = { - "plain": rules_as_str, - "rich": rules_as_rich, + "brief": rules_as_str, + "full": rules_as_rich, "md": rules_as_md, "docs": rules_as_docs, } - console.print(_rule_format_map[options.format](rules), highlight=False) + console.print( + _rule_format_map.get(options.format, rules_as_str)(rules), highlight=False + ) return 0 if options.list_tags: diff --git a/src/ansiblelint/cli.py b/src/ansiblelint/cli.py index cce5471546..628c9b4864 100644 --- a/src/ansiblelint/cli.py +++ b/src/ansiblelint/cli.py @@ -224,7 +224,7 @@ def get_cli_parser() -> argparse.ArgumentParser: default=False, action="store_true", help="List all the rules. For listing rules only the following formats " - "for argument -f are supported: {plain, rich, md}", + "for argument -f are supported: {brief, full, md} with 'brief' as default.", ) listing_group.add_argument( "-T", @@ -238,10 +238,11 @@ def get_cli_parser() -> argparse.ArgumentParser: "-f", "--format", dest="format", - default="rich", + default=None, choices=[ - "rich", - "plain", + "brief", + # "plain", + "full", "md", "json", "codeclimate", @@ -526,10 +527,16 @@ def get_config(arguments: list[str]) -> Namespace: options = parser.parse_args(arguments) # docs is not document, being used for internal documentation building - if options.list_rules and options.format not in ["plain", "rich", "md", "docs"]: + if options.list_rules and options.format not in [ + None, + "brief", + "full", + "md", + "docs", + ]: parser.error( f"argument -f: invalid choice: '{options.format}'. " - f"In combination with argument -L only 'plain', " + f"In combination with argument -L only 'brief', " f"'rich' or 'md' are supported with -f." ) diff --git a/src/ansiblelint/config.py b/src/ansiblelint/config.py index 528c19ffbc..461945f0c1 100644 --- a/src/ansiblelint/config.py +++ b/src/ansiblelint/config.py @@ -107,7 +107,7 @@ cwd=".", display_relative_path=True, exclude_paths=[], - format="rich", + format="brief", lintables=[], list_rules=False, list_tags=False, diff --git a/src/ansiblelint/generate_docs.py b/src/ansiblelint/generate_docs.py index a52e125c69..e8222ef32a 100644 --- a/src/ansiblelint/generate_docs.py +++ b/src/ansiblelint/generate_docs.py @@ -4,6 +4,7 @@ from typing import Iterable from rich import box +from rich.console import RenderableType # Remove this compatibility try-catch block once we drop support for rich < 10.7.0 try: @@ -70,9 +71,18 @@ def rules_as_docs(rules: RulesCollection) -> str: return "All markdown files for rules were dumped!" -def rules_as_str(rules: RulesCollection) -> str: +def rules_as_str(rules: RulesCollection) -> RenderableType: """Return rules as string.""" - return "\n".join([str(rule) for rule in rules.alphabetical()]) + table = Table(show_header=False, header_style="title", box=box.SIMPLE) + for rule in rules.alphabetical(): + if rule.tags: + tag = f"[dim] ({', '.join(rule.tags)})[/dim]" + else: + tag = "" + table.add_row( + f"[link={RULE_DOC_URL}{rule.id}/]{rule.id}[/link]", rule.shortdesc + tag + ) + return table def rules_as_md(rules: RulesCollection) -> str: diff --git a/test/test_list_rules.py b/test/test_list_rules.py index a72c04fc34..eab512bc4e 100644 --- a/test/test_list_rules.py +++ b/test/test_list_rules.py @@ -24,8 +24,8 @@ def test_list_rules_includes_opt_in_rules() -> None: @pytest.mark.parametrize( ("result", "returncode", "format_string"), ( - (False, 0, "plain"), - (False, 0, "rich"), + (False, 0, "brief"), + (False, 0, "full"), (False, 0, "md"), (True, 2, "json"), (True, 2, "codeclimate"), @@ -35,7 +35,7 @@ def test_list_rules_includes_opt_in_rules() -> None: ), ids=( "plain", - "rich", + "full", "md", "json", "codeclimate", diff --git a/test/test_rules_collection.py b/test/test_rules_collection.py index ea9fe330cc..60a2b83cc0 100644 --- a/test/test_rules_collection.py +++ b/test/test_rules_collection.py @@ -139,7 +139,7 @@ def test_rich_rule_listing() -> None: descriptions in the console output. """ rules_path = os.path.abspath("./test/rules/fixtures") - result = run_ansible_lint("-r", rules_path, "-f", "rich", "-L") + result = run_ansible_lint("-r", rules_path, "-f", "full", "-L") assert result.returncode == 0 for rule in RulesCollection([rules_path]): From 7ec6d6b856aa327edc6ddd3fdee383c0e57f1363 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 1 Feb 2023 12:42:22 -0600 Subject: [PATCH 36/36] Avoid caching on role_name regex (#2876)Co-authored-by: Sorin Sbarnea Co-authored-by: Sorin Sbarnea suppressing output affects repeated runs such as when using the `progressive` option Co-authored-by: Sorin Sbarnea --- src/ansiblelint/rules/role_name.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/ansiblelint/rules/role_name.py b/src/ansiblelint/rules/role_name.py index ed8d361205..bdbe3adec4 100644 --- a/src/ansiblelint/rules/role_name.py +++ b/src/ansiblelint/rules/role_name.py @@ -22,6 +22,7 @@ from __future__ import annotations import re +from functools import cache from pathlib import Path from typing import TYPE_CHECKING, Any @@ -34,13 +35,18 @@ from ansiblelint.errors import MatchError -ROLE_NAME_REGEX = r"^[a-z][a-z0-9_]*$" +ROLE_NAME_REGEX = re.compile(r"^[a-z][a-z0-9_]*$") def _remove_prefix(text: str, prefix: str) -> str: return re.sub(rf"^{re.escape(prefix)}", "", text) +@cache +def _match_role_name_regex(role_name: str) -> bool: + return ROLE_NAME_REGEX.match(role_name) is not None + + class RoleNames(AnsibleLintRule): # Unable to use f-strings due to flake8 bug with AST parsing """Role name {0} does not match ``^[a-z][a-z0-9_]*$`` pattern.""" @@ -52,14 +58,9 @@ class RoleNames(AnsibleLintRule): ) link = "https://docs.ansible.com/ansible/devel/dev_guide/developing_collections_structure.html#roles-directory" severity = "HIGH" - done: list[str] = [] # already noticed roles list tags = ["deprecations", "metadata"] version_added = "v6.8.5" - def __init__(self) -> None: - """Save precompiled regex.""" - self._re = re.compile(ROLE_NAME_REGEX) - def matchtask( self, task: dict[str, Any], file: Lintable | None = None ) -> list[MatchError]: @@ -95,15 +96,13 @@ def matchyaml(self, file: Lintable) -> list[MatchError]: ) role_name = _remove_prefix(role_name, "ansible-role-") - if role_name not in self.done: - self.done.append(role_name) - if role_name and not self._re.match(role_name): - result.append( - self.create_matcherror( - filename=file, - message=self.shortdesc.format(role_name), - ) + if role_name and not _match_role_name_regex(role_name): + result.append( + self.create_matcherror( + filename=file, + message=self.shortdesc.format(role_name), ) + ) return result @staticmethod