We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
command-instead-of-module "yum clean" revisited
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and main branch are affected too -->

##### Summary
<!--- Explain the problem briefly below -->
The lint error `command-instead-of-module` is raised for command `yum clean`. As the `yum` module documentation states there is no support for doing `yum clean` operation in the module. So the only option is to run a command.

This issue has been raised in the past as #369 and `warn: false` was recommended. But that is now deprecated in 2.11.

So shouldn't ansible-lint detect if `yum clean` is being run and not raise `command-instead-of-module`?

If there is positive feedback and nobody else wants to do it then I can volunteer to make the patch - it should be a simple change, no?

##### Issue Type

- Bug Report

##### Ansible and Ansible Lint details
<!--- Paste verbatim output between triple backticks -->
```console (paste below)
ansible --version
ansible [core 2.11.5]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/ansible/lib64/python3.6/site-packages/ansible
  ansible collection location = /usr/share/ansible/collections
  executable location = /opt/ansible/bin/ansible
  python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
  jinja version = 3.0.2
  libyaml = True

ansible-lint --version
ansible-lint 5.2.0 using ansible 2.11.5

```

- ansible installation method: pip
- ansible-lint installation method: pip

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
EL7

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
Have a command that runs `yum clean`.

<!--- Paste example playbooks or commands between triple backticks below -->
```yaml
  command:
    cmd: yum clean all
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### Desired Behaviour
<!--- Describe what you expected to happen when running the steps above -->
`command-instead-of-module` should not be raised for `yum clean` which cannot be done using the `yum` module.

Possible security bugs should be reported via email to `security@ansible.com`

##### Actual Behaviour
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
`command-instead-of-module` is raised even though the module cannot do this action.

<!--- Paste verbatim command output between triple backticks -->
```paste below

```


[minimum complete verifiable example]: http://stackoverflow.com/help/mcve


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/ansiblelint/rules/CommandsInsteadOfModulesRule.py`
Content:
```
1 # Copyright (c) 2013-2014 Will Thames <will@thames.id.au>
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
9 #
10 # The above copyright notice and this permission notice shall be included in
11 # all copies or substantial portions of the Software.
12 #
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 # THE SOFTWARE.
20 
21 import os
22 import sys
23 from typing import TYPE_CHECKING, Any, Dict, Union
24 
25 from ansiblelint.rules import AnsibleLintRule
26 from ansiblelint.utils import convert_to_boolean, get_first_cmd_arg, get_second_cmd_arg
27 
28 if TYPE_CHECKING:
29     from typing import Optional
30 
31     from ansiblelint.file_utils import Lintable
32 
33 
34 class CommandsInsteadOfModulesRule(AnsibleLintRule):
35     id = 'command-instead-of-module'
36     shortdesc = 'Using command rather than module'
37     description = (
38         'Executing a command when there is an Ansible module is generally a bad idea'
39     )
40     severity = 'HIGH'
41     tags = ['command-shell', 'idiom']
42     version_added = 'historic'
43 
44     _commands = ['command', 'shell']
45     _modules = {
46         'apt-get': 'apt-get',
47         'chkconfig': 'service',
48         'curl': 'get_url or uri',
49         'git': 'git',
50         'hg': 'hg',
51         'letsencrypt': 'acme_certificate',
52         'mktemp': 'tempfile',
53         'mount': 'mount',
54         'patch': 'patch',
55         'rpm': 'yum or rpm_key',
56         'rsync': 'synchronize',
57         'sed': 'template, replace or lineinfile',
58         'service': 'service',
59         'supervisorctl': 'supervisorctl',
60         'svn': 'subversion',
61         'systemctl': 'systemd',
62         'tar': 'unarchive',
63         'unzip': 'unarchive',
64         'wget': 'get_url or uri',
65         'yum': 'yum',
66     }
67 
68     _executable_options = {
69         'git': ['branch', 'log'],
70         'systemctl': ['set-default', 'show-environment', 'status'],
71     }
72 
73     def matchtask(
74         self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
75     ) -> Union[bool, str]:
76 
77         if task['action']['__ansible_module__'] not in self._commands:
78             return False
79 
80         first_cmd_arg = get_first_cmd_arg(task)
81         second_cmd_arg = get_second_cmd_arg(task)
82 
83         if not first_cmd_arg:
84             return False
85 
86         executable = os.path.basename(first_cmd_arg)
87 
88         if (
89             second_cmd_arg
90             and executable in self._executable_options
91             and second_cmd_arg in self._executable_options[executable]
92         ):
93             return False
94 
95         if executable in self._modules and convert_to_boolean(
96             task['action'].get('warn', True)
97         ):
98             message = '{0} used in place of {1} module'
99             return message.format(executable, self._modules[executable])
100         return False
101 
102 
103 if "pytest" in sys.modules:
104     import pytest
105 
106     from ansiblelint.testing import RunFromText  # pylint: disable=ungrouped-imports
107 
108     APT_GET = '''
109 - hosts: all
110   tasks:
111     - name: run apt-get update
112       command: apt-get update
113 '''
114 
115     GIT_BRANCH = '''
116 - hosts: all
117   tasks:
118     - name: print current git branch
119       command: git branch
120 '''
121 
122     GIT_LOG = '''
123 - hosts: all
124   tasks:
125     - name: print git log
126       command: git log
127 '''
128 
129     RESTART_SSHD = '''
130 - hosts: all
131   tasks:
132     - name: restart sshd
133       command: systemctl restart sshd
134 '''
135 
136     SYSTEMCTL_STATUS = '''
137 - hosts: all
138   tasks:
139     - name: show systemctl service status
140       command: systemctl status systemd-timesyncd
141 '''
142 
143     SYSTEMD_ENVIRONMENT = '''
144 - hosts: all
145   tasks:
146     - name: show systemd environment
147       command: systemctl show-environment
148 '''
149 
150     SYSTEMD_RUNLEVEL = '''
151 - hosts: all
152   tasks:
153     - name: set systemd runlevel
154       command: systemctl set-default multi-user.target
155 '''
156 
157     @pytest.mark.parametrize(
158         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
159     )
160     def test_apt_get(rule_runner: RunFromText) -> None:
161         """The apt module supports update."""
162         results = rule_runner.run_playbook(APT_GET)
163         assert len(results) == 1
164 
165     @pytest.mark.parametrize(
166         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
167     )
168     def test_restart_sshd(rule_runner: RunFromText) -> None:
169         """Restarting services is supported by the systemd module."""
170         results = rule_runner.run_playbook(RESTART_SSHD)
171         assert len(results) == 1
172 
173     @pytest.mark.parametrize(
174         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
175     )
176     def test_git_log(rule_runner: RunFromText) -> None:
177         """The git log command is not supported by the git module."""
178         results = rule_runner.run_playbook(GIT_LOG)
179         assert len(results) == 0
180 
181     @pytest.mark.parametrize(
182         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
183     )
184     def test_git_branch(rule_runner: RunFromText) -> None:
185         """The git branch command is not supported by the git module."""
186         results = rule_runner.run_playbook(GIT_BRANCH)
187         assert len(results) == 0
188 
189     @pytest.mark.parametrize(
190         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
191     )
192     def test_systemd_status(rule_runner: RunFromText) -> None:
193         """Set-default is not supported by the systemd module."""
194         results = rule_runner.run_playbook(SYSTEMCTL_STATUS)
195         assert len(results) == 0
196 
197     @pytest.mark.parametrize(
198         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
199     )
200     def test_systemd_environment(rule_runner: RunFromText) -> None:
201         """Showing the environment is not supported by the systemd module."""
202         results = rule_runner.run_playbook(SYSTEMD_ENVIRONMENT)
203         assert len(results) == 0
204 
205     @pytest.mark.parametrize(
206         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
207     )
208     def test_systemd_runlevel(rule_runner: RunFromText) -> None:
209         """Set-default is not supported by the systemd module."""
210         results = rule_runner.run_playbook(SYSTEMD_RUNLEVEL)
211         assert len(results) == 0
212
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py b/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py
--- a/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py
+++ b/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py
@@ -68,6 +68,7 @@
     _executable_options = {
         'git': ['branch', 'log'],
         'systemctl': ['set-default', 'show-environment', 'status'],
+        'yum': ['clean'],
     }
 
     def matchtask(
@@ -100,7 +101,7 @@
         return False
 
 
-if "pytest" in sys.modules:
+if "pytest" in sys.modules:  # noqa: C901
     import pytest
 
     from ansiblelint.testing import RunFromText  # pylint: disable=ungrouped-imports
@@ -154,6 +155,20 @@
       command: systemctl set-default multi-user.target
 '''
 
+    YUM_UPDATE = '''
+- hosts: all
+  tasks:
+    - name: run yum update
+      command: yum update
+'''
+
+    YUM_CLEAN = '''
+- hosts: all
+  tasks:
+    - name: clear yum cache
+      command: yum clean all
+'''
+
     @pytest.mark.parametrize(
         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
     )
@@ -209,3 +224,19 @@
         """Set-default is not supported by the systemd module."""
         results = rule_runner.run_playbook(SYSTEMD_RUNLEVEL)
         assert len(results) == 0
+
+    @pytest.mark.parametrize(
+        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
+    )
+    def test_yum_update(rule_runner: RunFromText) -> None:
+        """Using yum update should fail."""
+        results = rule_runner.run_playbook(YUM_UPDATE)
+        assert len(results) == 1
+
+    @pytest.mark.parametrize(
+        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']
+    )
+    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

{"golden_diff": "diff --git a/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py b/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py\n--- a/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py\n+++ b/src/ansiblelint/rules/CommandsInsteadOfModulesRule.py\n@@ -68,6 +68,7 @@\n     _executable_options = {\n         'git': ['branch', 'log'],\n         'systemctl': ['set-default', 'show-environment', 'status'],\n+        'yum': ['clean'],\n     }\n \n     def matchtask(\n@@ -100,7 +101,7 @@\n         return False\n \n \n-if \"pytest\" in sys.modules:\n+if \"pytest\" in sys.modules:  # noqa: C901\n     import pytest\n \n     from ansiblelint.testing import RunFromText  # pylint: disable=ungrouped-imports\n@@ -154,6 +155,20 @@\n       command: systemctl set-default multi-user.target\n '''\n \n+    YUM_UPDATE = '''\n+- hosts: all\n+  tasks:\n+    - name: run yum update\n+      command: yum update\n+'''\n+\n+    YUM_CLEAN = '''\n+- hosts: all\n+  tasks:\n+    - name: clear yum cache\n+      command: yum clean all\n+'''\n+\n     @pytest.mark.parametrize(\n         'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n     )\n@@ -209,3 +224,19 @@\n         \"\"\"Set-default is not supported by the systemd module.\"\"\"\n         results = rule_runner.run_playbook(SYSTEMD_RUNLEVEL)\n         assert len(results) == 0\n+\n+    @pytest.mark.parametrize(\n+        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n+    )\n+    def test_yum_update(rule_runner: RunFromText) -> None:\n+        \"\"\"Using yum update should fail.\"\"\"\n+        results = rule_runner.run_playbook(YUM_UPDATE)\n+        assert len(results) == 1\n+\n+    @pytest.mark.parametrize(\n+        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n+    )\n+    def test_yum_clean(rule_runner: RunFromText) -> None:\n+        \"\"\"The yum module does not support clearing yum cache.\"\"\"\n+        results = rule_runner.run_playbook(YUM_CLEAN)\n+        assert len(results) == 0\n", "issue": "command-instead-of-module \"yum clean\" revisited\n<!--- Verify first that your issue is not already reported on GitHub -->\r\n<!--- Also test if the latest release and main branch are affected too -->\r\n\r\n##### Summary\r\n<!--- Explain the problem briefly below -->\r\nThe lint error `command-instead-of-module` is raised for command `yum clean`. As the `yum` module documentation states there is no support for doing `yum clean` operation in the module. So the only option is to run a command.\r\n\r\nThis issue has been raised in the past as #369 and `warn: false` was recommended. But that is now deprecated in 2.11.\r\n\r\nSo shouldn't ansible-lint detect if `yum clean` is being run and not raise `command-instead-of-module`?\r\n\r\nIf there is positive feedback and nobody else wants to do it then I can volunteer to make the patch - it should be a simple change, no?\r\n\r\n##### Issue Type\r\n\r\n- Bug Report\r\n\r\n##### Ansible and Ansible Lint details\r\n<!--- Paste verbatim output between triple backticks -->\r\n```console (paste below)\r\nansible --version\r\nansible [core 2.11.5]\r\n  config file = /etc/ansible/ansible.cfg\r\n  configured module search path = ['/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /opt/ansible/lib64/python3.6/site-packages/ansible\r\n  ansible collection location = /usr/share/ansible/collections\r\n  executable location = /opt/ansible/bin/ansible\r\n  python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]\r\n  jinja version = 3.0.2\r\n  libyaml = True\r\n\r\nansible-lint --version\r\nansible-lint 5.2.0 using ansible 2.11.5\r\n\r\n```\r\n\r\n- ansible installation method: pip\r\n- ansible-lint installation method: pip\r\n\r\n##### OS / ENVIRONMENT\r\n<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->\r\nEL7\r\n\r\n##### STEPS TO REPRODUCE\r\n<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->\r\nHave a command that runs `yum clean`.\r\n\r\n<!--- Paste example playbooks or commands between triple backticks below -->\r\n```yaml\r\n  command:\r\n    cmd: yum clean all\r\n```\r\n\r\n<!--- HINT: You can paste gist.github.com links for larger files -->\r\n\r\n##### Desired Behaviour\r\n<!--- Describe what you expected to happen when running the steps above -->\r\n`command-instead-of-module` should not be raised for `yum clean` which cannot be done using the `yum` module.\r\n\r\nPossible security bugs should be reported via email to `security@ansible.com`\r\n\r\n##### Actual Behaviour\r\n<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->\r\n`command-instead-of-module` is raised even though the module cannot do this action.\r\n\r\n<!--- Paste verbatim command output between triple backticks -->\r\n```paste below\r\n\r\n```\r\n\r\n\r\n[minimum complete verifiable example]: http://stackoverflow.com/help/mcve\r\n\n", "before_files": [{"content": "# Copyright (c) 2013-2014 Will Thames <will@thames.id.au>\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n# THE SOFTWARE.\n\nimport os\nimport sys\nfrom typing import TYPE_CHECKING, Any, Dict, Union\n\nfrom ansiblelint.rules import AnsibleLintRule\nfrom ansiblelint.utils import convert_to_boolean, get_first_cmd_arg, get_second_cmd_arg\n\nif TYPE_CHECKING:\n    from typing import Optional\n\n    from ansiblelint.file_utils import Lintable\n\n\nclass CommandsInsteadOfModulesRule(AnsibleLintRule):\n    id = 'command-instead-of-module'\n    shortdesc = 'Using command rather than module'\n    description = (\n        'Executing a command when there is an Ansible module is generally a bad idea'\n    )\n    severity = 'HIGH'\n    tags = ['command-shell', 'idiom']\n    version_added = 'historic'\n\n    _commands = ['command', 'shell']\n    _modules = {\n        'apt-get': 'apt-get',\n        'chkconfig': 'service',\n        'curl': 'get_url or uri',\n        'git': 'git',\n        'hg': 'hg',\n        'letsencrypt': 'acme_certificate',\n        'mktemp': 'tempfile',\n        'mount': 'mount',\n        'patch': 'patch',\n        'rpm': 'yum or rpm_key',\n        'rsync': 'synchronize',\n        'sed': 'template, replace or lineinfile',\n        'service': 'service',\n        'supervisorctl': 'supervisorctl',\n        'svn': 'subversion',\n        'systemctl': 'systemd',\n        'tar': 'unarchive',\n        'unzip': 'unarchive',\n        'wget': 'get_url or uri',\n        'yum': 'yum',\n    }\n\n    _executable_options = {\n        'git': ['branch', 'log'],\n        'systemctl': ['set-default', 'show-environment', 'status'],\n    }\n\n    def matchtask(\n        self, task: Dict[str, Any], file: 'Optional[Lintable]' = None\n    ) -> Union[bool, str]:\n\n        if task['action']['__ansible_module__'] not in self._commands:\n            return False\n\n        first_cmd_arg = get_first_cmd_arg(task)\n        second_cmd_arg = get_second_cmd_arg(task)\n\n        if not first_cmd_arg:\n            return False\n\n        executable = os.path.basename(first_cmd_arg)\n\n        if (\n            second_cmd_arg\n            and executable in self._executable_options\n            and second_cmd_arg in self._executable_options[executable]\n        ):\n            return False\n\n        if executable in self._modules and convert_to_boolean(\n            task['action'].get('warn', True)\n        ):\n            message = '{0} used in place of {1} module'\n            return message.format(executable, self._modules[executable])\n        return False\n\n\nif \"pytest\" in sys.modules:\n    import pytest\n\n    from ansiblelint.testing import RunFromText  # pylint: disable=ungrouped-imports\n\n    APT_GET = '''\n- hosts: all\n  tasks:\n    - name: run apt-get update\n      command: apt-get update\n'''\n\n    GIT_BRANCH = '''\n- hosts: all\n  tasks:\n    - name: print current git branch\n      command: git branch\n'''\n\n    GIT_LOG = '''\n- hosts: all\n  tasks:\n    - name: print git log\n      command: git log\n'''\n\n    RESTART_SSHD = '''\n- hosts: all\n  tasks:\n    - name: restart sshd\n      command: systemctl restart sshd\n'''\n\n    SYSTEMCTL_STATUS = '''\n- hosts: all\n  tasks:\n    - name: show systemctl service status\n      command: systemctl status systemd-timesyncd\n'''\n\n    SYSTEMD_ENVIRONMENT = '''\n- hosts: all\n  tasks:\n    - name: show systemd environment\n      command: systemctl show-environment\n'''\n\n    SYSTEMD_RUNLEVEL = '''\n- hosts: all\n  tasks:\n    - name: set systemd runlevel\n      command: systemctl set-default multi-user.target\n'''\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_apt_get(rule_runner: RunFromText) -> None:\n        \"\"\"The apt module supports update.\"\"\"\n        results = rule_runner.run_playbook(APT_GET)\n        assert len(results) == 1\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_restart_sshd(rule_runner: RunFromText) -> None:\n        \"\"\"Restarting services is supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(RESTART_SSHD)\n        assert len(results) == 1\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_git_log(rule_runner: RunFromText) -> None:\n        \"\"\"The git log command is not supported by the git module.\"\"\"\n        results = rule_runner.run_playbook(GIT_LOG)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_git_branch(rule_runner: RunFromText) -> None:\n        \"\"\"The git branch command is not supported by the git module.\"\"\"\n        results = rule_runner.run_playbook(GIT_BRANCH)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_systemd_status(rule_runner: RunFromText) -> None:\n        \"\"\"Set-default is not supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(SYSTEMCTL_STATUS)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_systemd_environment(rule_runner: RunFromText) -> None:\n        \"\"\"Showing the environment is not supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(SYSTEMD_ENVIRONMENT)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_systemd_runlevel(rule_runner: RunFromText) -> None:\n        \"\"\"Set-default is not supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(SYSTEMD_RUNLEVEL)\n        assert len(results) == 0\n", "path": "src/ansiblelint/rules/CommandsInsteadOfModulesRule.py"}], "after_files": [{"content": "# Copyright (c) 2013-2014 Will Thames <will@thames.id.au>\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n# THE SOFTWARE.\n\nimport os\nimport sys\nfrom typing import TYPE_CHECKING, Any, Dict, Union\n\nfrom ansiblelint.rules import AnsibleLintRule\nfrom ansiblelint.utils import convert_to_boolean, get_first_cmd_arg, get_second_cmd_arg\n\nif TYPE_CHECKING:\n    from typing import Optional\n\n    from ansiblelint.file_utils import Lintable\n\n\nclass CommandsInsteadOfModulesRule(AnsibleLintRule):\n    id = 'command-instead-of-module'\n    shortdesc = 'Using command rather than module'\n    description = (\n        'Executing a command when there is an Ansible module is generally a bad idea'\n    )\n    severity = 'HIGH'\n    tags = ['command-shell', 'idiom']\n    version_added = 'historic'\n\n    _commands = ['command', 'shell']\n    _modules = {\n        'apt-get': 'apt-get',\n        'chkconfig': 'service',\n        'curl': 'get_url or uri',\n        'git': 'git',\n        'hg': 'hg',\n        'letsencrypt': 'acme_certificate',\n        'mktemp': 'tempfile',\n        'mount': 'mount',\n        'patch': 'patch',\n        'rpm': 'yum or rpm_key',\n        'rsync': 'synchronize',\n        'sed': 'template, replace or lineinfile',\n        'service': 'service',\n        'supervisorctl': 'supervisorctl',\n        'svn': 'subversion',\n        'systemctl': 'systemd',\n        'tar': 'unarchive',\n        'unzip': 'unarchive',\n        'wget': 'get_url or uri',\n        'yum': 'yum',\n    }\n\n    _executable_options = {\n        'git': ['branch', 'log'],\n        'systemctl': ['set-default', 'show-environment', 'status'],\n        'yum': ['clean'],\n    }\n\n    def matchtask(\n        self, task: Dict[str, Any], file: 'Optional[Lintable]' = None\n    ) -> Union[bool, str]:\n\n        if task['action']['__ansible_module__'] not in self._commands:\n            return False\n\n        first_cmd_arg = get_first_cmd_arg(task)\n        second_cmd_arg = get_second_cmd_arg(task)\n\n        if not first_cmd_arg:\n            return False\n\n        executable = os.path.basename(first_cmd_arg)\n\n        if (\n            second_cmd_arg\n            and executable in self._executable_options\n            and second_cmd_arg in self._executable_options[executable]\n        ):\n            return False\n\n        if executable in self._modules and convert_to_boolean(\n            task['action'].get('warn', True)\n        ):\n            message = '{0} used in place of {1} module'\n            return message.format(executable, self._modules[executable])\n        return False\n\n\nif \"pytest\" in sys.modules:  # noqa: C901\n    import pytest\n\n    from ansiblelint.testing import RunFromText  # pylint: disable=ungrouped-imports\n\n    APT_GET = '''\n- hosts: all\n  tasks:\n    - name: run apt-get update\n      command: apt-get update\n'''\n\n    GIT_BRANCH = '''\n- hosts: all\n  tasks:\n    - name: print current git branch\n      command: git branch\n'''\n\n    GIT_LOG = '''\n- hosts: all\n  tasks:\n    - name: print git log\n      command: git log\n'''\n\n    RESTART_SSHD = '''\n- hosts: all\n  tasks:\n    - name: restart sshd\n      command: systemctl restart sshd\n'''\n\n    SYSTEMCTL_STATUS = '''\n- hosts: all\n  tasks:\n    - name: show systemctl service status\n      command: systemctl status systemd-timesyncd\n'''\n\n    SYSTEMD_ENVIRONMENT = '''\n- hosts: all\n  tasks:\n    - name: show systemd environment\n      command: systemctl show-environment\n'''\n\n    SYSTEMD_RUNLEVEL = '''\n- hosts: all\n  tasks:\n    - name: set systemd runlevel\n      command: systemctl set-default multi-user.target\n'''\n\n    YUM_UPDATE = '''\n- hosts: all\n  tasks:\n    - name: run yum update\n      command: yum update\n'''\n\n    YUM_CLEAN = '''\n- hosts: all\n  tasks:\n    - name: clear yum cache\n      command: yum clean all\n'''\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_apt_get(rule_runner: RunFromText) -> None:\n        \"\"\"The apt module supports update.\"\"\"\n        results = rule_runner.run_playbook(APT_GET)\n        assert len(results) == 1\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_restart_sshd(rule_runner: RunFromText) -> None:\n        \"\"\"Restarting services is supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(RESTART_SSHD)\n        assert len(results) == 1\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_git_log(rule_runner: RunFromText) -> None:\n        \"\"\"The git log command is not supported by the git module.\"\"\"\n        results = rule_runner.run_playbook(GIT_LOG)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_git_branch(rule_runner: RunFromText) -> None:\n        \"\"\"The git branch command is not supported by the git module.\"\"\"\n        results = rule_runner.run_playbook(GIT_BRANCH)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_systemd_status(rule_runner: RunFromText) -> None:\n        \"\"\"Set-default is not supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(SYSTEMCTL_STATUS)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_systemd_environment(rule_runner: RunFromText) -> None:\n        \"\"\"Showing the environment is not supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(SYSTEMD_ENVIRONMENT)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_systemd_runlevel(rule_runner: RunFromText) -> None:\n        \"\"\"Set-default is not supported by the systemd module.\"\"\"\n        results = rule_runner.run_playbook(SYSTEMD_RUNLEVEL)\n        assert len(results) == 0\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_yum_update(rule_runner: RunFromText) -> None:\n        \"\"\"Using yum update should fail.\"\"\"\n        results = rule_runner.run_playbook(YUM_UPDATE)\n        assert len(results) == 1\n\n    @pytest.mark.parametrize(\n        'rule_runner', (CommandsInsteadOfModulesRule,), indirect=['rule_runner']\n    )\n    def test_yum_clean(rule_runner: RunFromText) -> None:\n        \"\"\"The yum module does not support clearing yum cache.\"\"\"\n        results = rule_runner.run_playbook(YUM_CLEAN)\n        assert len(results) == 0\n", "path": "src/ansiblelint/rules/CommandsInsteadOfModulesRule.py"}]}