prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  False positive: async jobs
  <!--- Verify first that your issue is not already reported on GitHub -->
  <!--- Also test if the latest release and master branch are affected too -->
  
  ##### Summary
  <!--- Explain the problem briefly below -->
  A `command` module task that is run as an async job is incorrectly treated as a normal sync task.
  
  For async tasks the options like `changed_when` (and `failed_when` and so on) are not given to the async `command` task itself, they are given to the `async_status` module task that is run after the async task.
  
  Ansible-lint does not understand this and complains for rule `no-changed-when` for the `command` task.
  
  Example:
  ```yaml
  ---
  - name: Asynchronous long task
    command: alongtask.sh
    async: 1000
    poll: 0
    register: job_sleeper
  
  - name: Wait for asynchronous job to end
    async_status:
      jid: '{{ job_sleeper.ansible_job_id }}'
    register: job_result
    until: job_result.finished
    retries: 100
    delay: 10
    changed_when: [....]
  ```
  
  Note how the `changed_when` is given in the `async_status` task and not in the `command` task.
  
  ##### Issue Type
  
  - Bug Report
  
  ##### Ansible and Ansible Lint details
  <!--- Paste verbatim output between triple backticks -->
  ```console (paste below)
  ansible --version
  2.9.21
  
  ansible-lint --version
  5.0.8
  
  ```
  
  - 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.9 all updated
  
  ##### STEPS TO REPRODUCE
  <!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
  ```yaml
  ---
  - name: Asynchronous yum task
    command: alongtask.sh
    async: 1000
    poll: 0
    register: job_sleeper
  
  - name: Wait for asynchronous job to end
    async_status:
      jid: '{{ job_sleeper.ansible_job_id }}'
    register: job_result
    until: job_result.finished
    retries: 100
    delay: 10
    changed_when: [....]
  ```
  
  <!--- Paste example playbooks or commands between triple backticks below -->
  ```console (paste below)
  
  ```
  
  <!--- HINT: You can paste gist.github.com links for larger files -->
  
  ##### Desired Behaviour
  <!--- Describe what you expected to happen when running the steps above -->
  Ansible-lint should not detect `no-changed-when` for `command` module task run as async job since the `changed_when` cannot be given to the `command` module task itself.
  
  It should detect that there is a `changed_when` in the following `async_status` task.
  
  ##### Actual Behaviour
  <!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
  Ansible-lint detects false positive `no-changed-when` for `command` module task run as async job even though `changed_when` cannot be correctly given for an async task - the `changed_when` is given for the subsequent `async_status` module task.
  
  <!--- 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/CommandHasChangesCheckRule.py`
  Content:
  ```
  1 # Copyright (c) 2016 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 from typing import TYPE_CHECKING, Any, Dict, Union
  22 
  23 from ansiblelint.rules import AnsibleLintRule
  24 
  25 if TYPE_CHECKING:
  26     from typing import Optional
  27 
  28     from ansiblelint.file_utils import Lintable
  29 
  30 
  31 class CommandHasChangesCheckRule(AnsibleLintRule):
  32     id = 'no-changed-when'
  33     shortdesc = 'Commands should not change things if nothing needs doing'
  34     description = (
  35         'Commands should either read information (and thus set '
  36         '``changed_when``) or not do something if it has already been '
  37         'done (using creates/removes) or only do it if another '
  38         'check has a particular result (``when``)'
  39     )
  40     severity = 'HIGH'
  41     tags = ['command-shell', 'idempotency']
  42     version_added = 'historic'
  43 
  44     _commands = ['command', 'shell', 'raw']
  45 
  46     def matchtask(
  47         self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
  48     ) -> Union[bool, str]:
  49         if task["__ansible_action_type__"] == 'task':
  50             if task["action"]["__ansible_module__"] in self._commands:
  51                 return (
  52                     'changed_when' not in task
  53                     and 'when' not in task
  54                     and 'creates' not in task['action']
  55                     and 'removes' not in task['action']
  56                 )
  57         return False
  58
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/src/ansiblelint/rules/CommandHasChangesCheckRule.py b/src/ansiblelint/rules/CommandHasChangesCheckRule.py
  --- a/src/ansiblelint/rules/CommandHasChangesCheckRule.py
  +++ b/src/ansiblelint/rules/CommandHasChangesCheckRule.py
  @@ -53,5 +53,6 @@
                       and 'when' not in task
                       and 'creates' not in task['action']
                       and 'removes' not in task['action']
  +                    and not ('async' in task and task.get('poll') == 0)
                   )
           return False
  
verification_info: {"golden_diff": "diff --git a/src/ansiblelint/rules/CommandHasChangesCheckRule.py b/src/ansiblelint/rules/CommandHasChangesCheckRule.py\n--- a/src/ansiblelint/rules/CommandHasChangesCheckRule.py\n+++ b/src/ansiblelint/rules/CommandHasChangesCheckRule.py\n@@ -53,5 +53,6 @@\n                     and 'when' not in task\n                     and 'creates' not in task['action']\n                     and 'removes' not in task['action']\n+                    and not ('async' in task and task.get('poll') == 0)\n                 )\n         return False\n", "issue": "False positive: async jobs\n<!--- Verify first that your issue is not already reported on GitHub -->\r\n<!--- Also test if the latest release and master branch are affected too -->\r\n\r\n##### Summary\r\n<!--- Explain the problem briefly below -->\r\nA `command` module task that is run as an async job is incorrectly treated as a normal sync task.\r\n\r\nFor async tasks the options like `changed_when` (and `failed_when` and so on) are not given to the async `command` task itself, they are given to the `async_status` module task that is run after the async task.\r\n\r\nAnsible-lint does not understand this and complains for rule `no-changed-when` for the `command` task.\r\n\r\nExample:\r\n```yaml\r\n---\r\n- name: Asynchronous long task\r\n  command: alongtask.sh\r\n  async: 1000\r\n  poll: 0\r\n  register: job_sleeper\r\n\r\n- name: Wait for asynchronous job to end\r\n  async_status:\r\n    jid: '{{ job_sleeper.ansible_job_id }}'\r\n  register: job_result\r\n  until: job_result.finished\r\n  retries: 100\r\n  delay: 10\r\n  changed_when: [....]\r\n```\r\n\r\nNote how the `changed_when` is given in the `async_status` task and not in the `command` task.\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\n2.9.21\r\n\r\nansible-lint --version\r\n5.0.8\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.9 all updated\r\n\r\n##### STEPS TO REPRODUCE\r\n<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->\r\n```yaml\r\n---\r\n- name: Asynchronous yum task\r\n  command: alongtask.sh\r\n  async: 1000\r\n  poll: 0\r\n  register: job_sleeper\r\n\r\n- name: Wait for asynchronous job to end\r\n  async_status:\r\n    jid: '{{ job_sleeper.ansible_job_id }}'\r\n  register: job_result\r\n  until: job_result.finished\r\n  retries: 100\r\n  delay: 10\r\n  changed_when: [....]\r\n```\r\n\r\n<!--- Paste example playbooks or commands between triple backticks below -->\r\n```console (paste below)\r\n\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\nAnsible-lint should not detect `no-changed-when` for `command` module task run as async job since the `changed_when` cannot be given to the `command` module task itself.\r\n\r\nIt should detect that there is a `changed_when` in the following `async_status` task.\r\n\r\n##### Actual Behaviour\r\n<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->\r\nAnsible-lint detects false positive `no-changed-when` for `command` module task run as async job even though `changed_when` cannot be correctly given for an async task - the `changed_when` is given for the subsequent `async_status` module task.\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) 2016 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\nfrom typing import TYPE_CHECKING, Any, Dict, Union\n\nfrom ansiblelint.rules import AnsibleLintRule\n\nif TYPE_CHECKING:\n    from typing import Optional\n\n    from ansiblelint.file_utils import Lintable\n\n\nclass CommandHasChangesCheckRule(AnsibleLintRule):\n    id = 'no-changed-when'\n    shortdesc = 'Commands should not change things if nothing needs doing'\n    description = (\n        'Commands should either read information (and thus set '\n        '``changed_when``) or not do something if it has already been '\n        'done (using creates/removes) or only do it if another '\n        'check has a particular result (``when``)'\n    )\n    severity = 'HIGH'\n    tags = ['command-shell', 'idempotency']\n    version_added = 'historic'\n\n    _commands = ['command', 'shell', 'raw']\n\n    def matchtask(\n        self, task: Dict[str, Any], file: 'Optional[Lintable]' = None\n    ) -> Union[bool, str]:\n        if task[\"__ansible_action_type__\"] == 'task':\n            if task[\"action\"][\"__ansible_module__\"] in self._commands:\n                return (\n                    'changed_when' not in task\n                    and 'when' not in task\n                    and 'creates' not in task['action']\n                    and 'removes' not in task['action']\n                )\n        return False\n", "path": "src/ansiblelint/rules/CommandHasChangesCheckRule.py"}], "after_files": [{"content": "# Copyright (c) 2016 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\nfrom typing import TYPE_CHECKING, Any, Dict, Union\n\nfrom ansiblelint.rules import AnsibleLintRule\n\nif TYPE_CHECKING:\n    from typing import Optional\n\n    from ansiblelint.file_utils import Lintable\n\n\nclass CommandHasChangesCheckRule(AnsibleLintRule):\n    id = 'no-changed-when'\n    shortdesc = 'Commands should not change things if nothing needs doing'\n    description = (\n        'Commands should either read information (and thus set '\n        '``changed_when``) or not do something if it has already been '\n        'done (using creates/removes) or only do it if another '\n        'check has a particular result (``when``)'\n    )\n    severity = 'HIGH'\n    tags = ['command-shell', 'idempotency']\n    version_added = 'historic'\n\n    _commands = ['command', 'shell', 'raw']\n\n    def matchtask(\n        self, task: Dict[str, Any], file: 'Optional[Lintable]' = None\n    ) -> Union[bool, str]:\n        if task[\"__ansible_action_type__\"] == 'task':\n            if task[\"action\"][\"__ansible_module__\"] in self._commands:\n                return (\n                    'changed_when' not in task\n                    and 'when' not in task\n                    and 'creates' not in task['action']\n                    and 'removes' not in task['action']\n                    and not ('async' in task and task.get('poll') == 0)\n                )\n        return False\n", "path": "src/ansiblelint/rules/CommandHasChangesCheckRule.py"}]}