Skip to content

Commit

Permalink
add 'file: Lintable' argument to BaseRule.matchtask method (#1535)
Browse files Browse the repository at this point in the history
Add 'file: Lintable' argument to matchtask method of the rule classes
which is None by default, because there are cases that it is necessary
to do some analyses depends on the context, that is, file: Lintable,
like the followings.

- want to restrict tasks in 'main.yml' use include_tasks only
- want to restrict the level of inclusions with include_tasks to 2

Signed-Off-By: Satoru SATOH <satoru.satoh@gmail.com>
  • Loading branch information
ssato committed Apr 20, 2021
1 parent f596ddc commit eab3e1b
Show file tree
Hide file tree
Showing 26 changed files with 203 additions and 52 deletions.
11 changes: 9 additions & 2 deletions examples/rules/TaskHasTag.py
@@ -1,8 +1,13 @@
"""Example implementation of a rule requiring tasks to have tags set."""
from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class TaskHasTag(AnsibleLintRule):
"""Tasks must have tag."""
Expand All @@ -12,7 +17,9 @@ class TaskHasTag(AnsibleLintRule):
description = 'Tasks must have tag'
tags = ['productivity', 'tags']

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
"""Task matching method."""
if isinstance(task, str):
return False
Expand Down
6 changes: 5 additions & 1 deletion src/ansiblelint/_internal/rules.py
Expand Up @@ -2,6 +2,8 @@
from typing import TYPE_CHECKING, Any, Dict, List, Union

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.constants import odict
from ansiblelint.errors import MatchError
from ansiblelint.file_utils import Lintable
Expand Down Expand Up @@ -43,7 +45,9 @@ def matchlines(self, file: "Lintable") -> List["MatchError"]:
"""Return matches found for a specific line."""
return []

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: "Optional[Lintable]" = None
) -> Union[bool, str]:
"""Confirm if current rule is matching a specific task."""
return False

Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/CommandHasChangesCheckRule.py
Expand Up @@ -18,10 +18,15 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class CommandHasChangesCheckRule(AnsibleLintRule):
id = 'no-changed-when'
Expand All @@ -38,7 +43,9 @@ class CommandHasChangesCheckRule(AnsibleLintRule):

_commands = ['command', 'shell', 'raw']

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
if task["__ansible_action_type__"] == 'task':
if task["action"]["__ansible_module__"] in self._commands:
return (
Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/CommandsInsteadOfArgumentsRule.py
Expand Up @@ -19,11 +19,16 @@
# THE SOFTWARE.

import os
from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule
from ansiblelint.utils import convert_to_boolean, get_first_cmd_arg

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class CommandsInsteadOfArgumentsRule(AnsibleLintRule):
id = 'deprecated-command-syntax'
Expand All @@ -47,7 +52,9 @@ class CommandsInsteadOfArgumentsRule(AnsibleLintRule):
'rm': 'state=absent',
}

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
if task["action"]["__ansible_module__"] in self._commands:
first_cmd_arg = get_first_cmd_arg(task)
if not first_cmd_arg:
Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/CommandsInsteadOfModulesRule.py
Expand Up @@ -19,11 +19,16 @@
# THE SOFTWARE.

import os
from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule
from ansiblelint.utils import convert_to_boolean, get_first_cmd_arg

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class CommandsInsteadOfModulesRule(AnsibleLintRule):
id = 'command-instead-of-module'
Expand Down Expand Up @@ -59,7 +64,9 @@ class CommandsInsteadOfModulesRule(AnsibleLintRule):
'yum': 'yum',
}

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
if task['action']['__ansible_module__'] not in self._commands:
return False

Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/ComparisonToEmptyStringRule.py
Expand Up @@ -3,11 +3,16 @@

import re
import sys
from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule
from ansiblelint.utils import nested_items

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class ComparisonToEmptyStringRule(AnsibleLintRule):
id = 'empty-string-compare'
Expand All @@ -22,7 +27,9 @@ class ComparisonToEmptyStringRule(AnsibleLintRule):

empty_string_compare = re.compile("[=!]= ?(\"{2}|'{2})")

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
for k, v, _ in nested_items(task):
if k == 'when':
if isinstance(v, str):
Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/ComparisonToLiteralBoolRule.py
Expand Up @@ -2,11 +2,16 @@
# Copyright (c) 2018-2021, Ansible Project

import re
from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule
from ansiblelint.utils import nested_items

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class ComparisonToLiteralBoolRule(AnsibleLintRule):
id = 'literal-compare'
Expand All @@ -21,7 +26,9 @@ class ComparisonToLiteralBoolRule(AnsibleLintRule):

literal_bool_compare = re.compile("[=!]= ?(True|true|False|false)")

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
for k, v, _ in nested_items(task):
if k == 'when':
if isinstance(v, str):
Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/DeprecatedModuleRule.py
@@ -1,9 +1,14 @@
# Copyright (c) 2018, Ansible Project

from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class DeprecatedModuleRule(AnsibleLintRule):
id = 'deprecated-module'
Expand Down Expand Up @@ -60,7 +65,9 @@ class DeprecatedModuleRule(AnsibleLintRule):
'include',
]

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
module = task["action"]["__ansible_module__"]
if module in self._modules:
message = '{0} {1}'
Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/EnvVarsInCommandRule.py
Expand Up @@ -18,11 +18,16 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule
from ansiblelint.utils import FILENAME_KEY, LINE_NUMBER_KEY, get_first_cmd_arg

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class EnvVarsInCommandRule(AnsibleLintRule):
id = 'inline-env-var'
Expand Down Expand Up @@ -51,7 +56,9 @@ class EnvVarsInCommandRule(AnsibleLintRule):
FILENAME_KEY,
]

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
if task["action"]["__ansible_module__"] in ['command']:
first_cmd_arg = get_first_cmd_arg(task)
if not first_cmd_arg:
Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/GitHasVersionRule.py
Expand Up @@ -18,10 +18,15 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class GitHasVersionRule(AnsibleLintRule):
id = 'git-latest'
Expand All @@ -34,7 +39,9 @@ class GitHasVersionRule(AnsibleLintRule):
tags = ['idempotency']
version_added = 'historic'

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
return bool(
task['action']['__ansible_module__'] == 'git'
and task['action'].get('version', 'HEAD') == 'HEAD'
Expand Down
11 changes: 9 additions & 2 deletions src/ansiblelint/rules/MercurialHasRevisionRule.py
Expand Up @@ -18,10 +18,15 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class MercurialHasRevisionRule(AnsibleLintRule):
id = 'hg-latest'
Expand All @@ -34,7 +39,9 @@ class MercurialHasRevisionRule(AnsibleLintRule):
tags = ['idempotency']
version_added = 'historic'

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
return bool(
task['action']['__ansible_module__'] == 'hg'
and task['action'].get('revision', 'default') == 'default'
Expand Down
12 changes: 10 additions & 2 deletions src/ansiblelint/rules/MissingFilePermissionsRule.py
Expand Up @@ -17,10 +17,16 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


# Despite documentation mentioning 'preserve' only these modules support it:
_modules_with_preserve = (
'copy',
Expand Down Expand Up @@ -60,7 +66,9 @@ class MissingFilePermissionsRule(AnsibleLintRule):
'lineinfile': False,
}

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
module = task["action"]["__ansible_module__"]
mode = task['action'].get('mode', None)

Expand Down
12 changes: 9 additions & 3 deletions src/ansiblelint/rules/NestedJinjaRule.py
Expand Up @@ -22,10 +22,15 @@
# THE SOFTWARE.

import re
from typing import Any, Dict, Union
from typing import TYPE_CHECKING, Any, Dict, Union

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.file_utils import Lintable


class NestedJinjaRule(AnsibleLintRule):
id = 'no-jinja-nesting'
Expand All @@ -42,8 +47,9 @@ class NestedJinjaRule(AnsibleLintRule):

pattern = re.compile(r"{{(?:[^{}]*)?[^'\"]{{")

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:

def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
command = "".join(
str(value)
# task properties are stored in the 'action' key
Expand Down
8 changes: 6 additions & 2 deletions src/ansiblelint/rules/NoFormattingInWhenRule.py
Expand Up @@ -3,6 +3,8 @@
from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.constants import odict
from ansiblelint.errors import MatchError
from ansiblelint.file_utils import Lintable
Expand Down Expand Up @@ -31,7 +33,7 @@ def matchplay(
if 'roles' not in data or data['roles'] is None:
return errors
for role in data['roles']:
if self.matchtask(role):
if self.matchtask(role, file=file):
errors.append(self.create_matcherror(details=str({'when': role})))
if isinstance(data, list):
for play_item in data:
Expand All @@ -40,5 +42,7 @@ def matchplay(
errors = errors + sub_errors
return errors

def matchtask(self, task: Dict[str, Any]) -> Union[bool, str]:
def matchtask(
self, task: Dict[str, Any], file: 'Optional[Lintable]' = None
) -> Union[bool, str]:
return 'when' in task and not self._is_valid(task['when'])

0 comments on commit eab3e1b

Please sign in to comment.