Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Normalize version field for rules #4431

Merged
merged 1 commit into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/ansiblelint/_internal/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any

from packaging.version import InvalidVersion, Version

from ansiblelint.constants import RULE_DOC_URL

if TYPE_CHECKING:
Expand Down Expand Up @@ -41,7 +43,7 @@ class BaseRule:
id: str = ""
tags: list[str] = []
description: str = ""
version_added: str = ""
version_changed: str = ""
severity: str = ""
link: str = ""
has_dynamic_tags: bool = False
Expand All @@ -59,6 +61,13 @@ class BaseRule:
# Allow rules to provide a custom short description instead of using __doc__
_shortdesc: str = ""

def __init__(self) -> None:
try:
Version(self.version_changed)
except InvalidVersion:
msg = f"Rule {self.__class__.__name__} has an invalid version_changed field '{self.version_changed}', is should be a 'X.Y.Z' format value."
_logger.warning(msg)

@property
def help(self) -> str:
"""Return a help markdown string for the rule."""
Expand Down Expand Up @@ -195,7 +204,7 @@ class RuntimeErrorRule(BaseRule):
_shortdesc = "Unexpected internal error"
severity = "VERY_HIGH"
tags = ["core"]
version_added = "v5.0.0"
version_changed = "5.0.0"
_order = 0
unloadable = True

Expand All @@ -207,7 +216,7 @@ class AnsibleParserErrorRule(BaseRule):
description = "Ansible parser fails; this usually indicates an invalid file."
severity = "VERY_HIGH"
tags = ["core"]
version_added = "v5.0.0"
version_changed = "5.0.0"
_order = 0
unloadable = True

Expand All @@ -219,7 +228,7 @@ class LoadingFailureRule(BaseRule):
description = "Linter failed to process a file, possible invalid file."
severity = "VERY_HIGH"
tags = ["core", "unskippable"]
version_added = "v4.3.0"
version_changed = "4.3.0"
_help = LOAD_FAILURE_MD
_order = 0
_ids = {
Expand All @@ -235,6 +244,6 @@ class WarningRule(BaseRule):
severity = "LOW"
# should remain experimental as that would keep it warning only
tags = ["core", "experimental"]
version_added = "v6.8.0"
version_changed = "6.8.0"
_order = 0
unloadable = True
4 changes: 2 additions & 2 deletions src/ansiblelint/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ def rules_as_rich(rules: RulesCollection) -> Iterable[Table]:
if rule.link:
description += f" [(more)]({rule.link})"
table.add_row("description", Markdown(description))
if rule.version_added:
table.add_row("version_added", rule.version_added)
if rule.version_changed:
table.add_row("version_changed", rule.version_changed)
if rule.tags:
table.add_row("tags", ", ".join(rule.tags))
if rule.severity:
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class ArgsRule(AnsibleLintRule):
severity = "HIGH"
description = "Check whether tasks are using correct module options."
tags = ["syntax", "experimental"]
version_added = "v6.10.0"
version_changed = "6.10.0"
module_aliases: dict[str, str] = {"block/always/rescue": "block/always/rescue"}
_ids = {
"args[module]": description,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/avoid_implicit.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class AvoidImplicitRule(AnsibleLintRule):
)
severity = "MEDIUM"
tags = ["unpredictability"]
version_added = "v6.8.0"
version_changed = "6.8.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/command_instead_of_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CommandsInsteadOfModulesRule(AnsibleLintRule):
)
severity = "HIGH"
tags = ["command-shell", "idiom"]
version_added = "historic"
version_changed = "24.10.0"

_commands = ["command", "shell"]
_modules = {
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/command_instead_of_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class UseCommandInsteadOfShellRule(AnsibleLintRule, TransformMixin):
)
severity = "HIGH"
tags = ["command-shell", "idiom"]
version_added = "historic"
version_changed = "6.18.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/complexity.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ComplexityRule(AnsibleLintRule):
description = "There should be limited tasks executed inside any file"
severity = "MEDIUM"
tags = ["experimental", "idiom"]
version_added = "v6.18.0 (last update)"
version_changed = "6.18.0"
_re_templated_inside = re.compile(r".*\{\{.*\}\}.*\w.*$")

def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]:
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/deprecated_bare_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class UsingBareVariablesIsDeprecatedRule(AnsibleLintRule):
)
severity = "VERY_HIGH"
tags = ["deprecations"]
version_added = "historic"
version_changed = "6.19.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/deprecated_local_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TaskNoLocalActionRule(AnsibleLintRule, TransformMixin):
needs_raw_task = True
severity = "MEDIUM"
tags = ["deprecations"]
version_added = "v4.0.0"
version_changed = "4.0.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/deprecated_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class DeprecatedModuleRule(AnsibleLintRule):
link = "https://docs.ansible.com/ansible/latest/collections/index_module.html"
severity = "HIGH"
tags = ["deprecations"]
version_added = "v4.0.0"
version_changed = "4.0.0"

_modules = [
"accelerate",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/empty_string_compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ComparisonToEmptyStringRule(AnsibleLintRule):
)
severity = "HIGH"
tags = ["idiom", "opt-in"]
version_added = "v4.0.0"
version_changed = "4.0.0"

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

Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/fqcn.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class FQCNBuiltinsRule(AnsibleLintRule, TransformMixin):
"Check whether actions are using using full qualified collection names."
)
tags = ["formatting"]
version_added = "v6.8.0"
version_changed = "6.8.0"
module_aliases: dict[str, str] = {"block/always/rescue": "block/always/rescue"}
_ids = {
"fqcn[action-core]": "Use FQCN for builtin module actions",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/galaxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class GalaxyRule(AnsibleLintRule):
description = "Confirm that collection's units are valid."
severity = "MEDIUM"
tags = ["metadata"]
version_added = "v6.11.0 (last update)"
version_changed = "6.11.0"
_ids = {
"galaxy[tags]": "galaxy.yaml must have one of the required tags",
"galaxy[no-changelog]": "No changelog found. Please add a changelog file. Refer to the galaxy.md file for more info.",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/galaxy_version_incorrect.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class GalaxyVersionIncorrectRule(AnsibleLintRule):
description = "Confirm via galaxy.yml file if collection version is greater than or equal to 1.0.0."
severity = "MEDIUM"
tags = ["opt-in", "metadata"]
version_added = "v24.7.0"
version_changed = "24.7.0"

def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]:
"""Return matches found for a specific play (entry in playbook)."""
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/ignore_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class IgnoreErrorsRule(AnsibleLintRule):
)
severity = "LOW"
tags = ["unpredictability"]
version_added = "v5.0.7"
version_changed = "5.0.7"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/inline_env_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class EnvVarsInCommandRule(AnsibleLintRule):
)
severity = "VERY_HIGH"
tags = ["command-shell", "idiom"]
version_added = "historic"
version_changed = "5.0.11"

expected_args = [
"chdir",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class JinjaRule(AnsibleLintRule, TransformMixin):
id = "jinja"
severity = "LOW"
tags = ["formatting"]
version_added = "v6.5.0"
version_changed = "6.5.0"
_ansible_error_re = re.compile(
(
r"^(?P<error>.*): (?P<detail>.*)\. String: (?P<string>.*)$"
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/key_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class KeyOrderRule(AnsibleLintRule, TransformMixin):
id = "key-order"
severity = "LOW"
tags = ["formatting"]
version_added = "v6.6.2"
version_changed = "6.6.2"
needs_raw_task = True
_ids = {
"key-order[task]": "You can improve the task key order",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/latest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class LatestRule(AnsibleLintRule):
)
severity = "MEDIUM"
tags = ["idempotency"]
version_added = "v6.5.2"
version_changed = "6.5.2"
_ids = {
"latest[git]": "Use a commit hash or tag instead of 'latest' for git",
"latest[hg]": "Use a commit hash or tag instead of 'latest' for hg",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/literal_compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ComparisonToLiteralBoolRule(AnsibleLintRule):
)
severity = "HIGH"
tags = ["idiom"]
version_added = "v4.0.0"
version_changed = "4.0.0"

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

Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/rules/loop_var_prefix.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class RoleLoopVarPrefix(AnsibleLintRule):
"loop-var-prefix[wrong]": "Loop variable name does not match regex.",
"loop-var-prefix[missing]": "Replace unsafe implicit `item` loop variable.",
}
version_changed = "6.7.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/meta_incorrect.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class MetaChangeFromDefaultRule(AnsibleLintRule):
)
severity = "HIGH"
tags = ["metadata"]
version_added = "v4.0.0"
version_changed = "4.0.0"

def matchyaml(self, file: Lintable) -> list[MatchError]:
if file.kind != "meta" or not file.data:
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/meta_no_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class MetaTagValidRule(AnsibleLintRule):
)
severity = "HIGH"
tags = ["metadata"]
version_added = "v4.0.0"
version_changed = "4.0.0"

TAG_REGEXP = re.compile(r"^[a-z0-9]+$")

Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/meta_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class CheckRequiresAnsibleVersion(AnsibleLintRule):
)
severity = "VERY_HIGH"
tags = ["metadata"]
version_added = "v6.11.0 (last update)"
version_changed = "6.11.0"

_ids = {
"meta-runtime[unsupported-version]": "'requires_ansible' key must refer to a currently supported version",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/meta_video_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MetaVideoLinksRule(AnsibleLintRule):
)
severity = "LOW"
tags = ["metadata"]
version_added = "v4.0.0"
version_changed = "4.0.0"

VIDEO_REGEXP = {
"google": re.compile(r"https://drive\.google\.com.*file/d/([0-9A-Za-z-_]+)/.*"),
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/name.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class NameRule(AnsibleLintRule, TransformMixin):
)
severity = "MEDIUM"
tags = ["idiom"]
version_added = "v6.9.1 (last update)"
version_changed = "6.9.1"
_re_templated_inside = re.compile(r".*\{\{.*\}\}.*\w.*$")
_ids = {
"name[play]": "All plays should be named.",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_changed_when.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class CommandHasChangesCheckRule(AnsibleLintRule):
id = "no-changed-when"
severity = "HIGH"
tags = ["command-shell", "idempotency"]
version_added = "historic"
version_changed = "6.14.5"

_commands = [
"ansible.builtin.command",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_free_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class NoFreeFormRule(AnsibleLintRule, TransformMixin):
description = "Avoid free-form inside files as it can produce subtle bugs."
severity = "MEDIUM"
tags = ["syntax", "risk"]
version_added = "v6.8.0"
version_changed = "6.8.0"
needs_raw_task = True
cmd_shell_re = re.compile(
r"(chdir|creates|executable|removes|stdin|stdin_add_newline|warn)=",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class UseHandlerRatherThanWhenChangedRule(AnsibleLintRule):
link = "https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html#handlers"
severity = "MEDIUM"
tags = ["idiom"]
version_added = "historic"
version_changed = "24.10.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_jinja_when.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class NoFormattingInWhenRule(AnsibleLintRule, TransformMixin):
)
severity = "HIGH"
tags = ["deprecations"]
version_added = "historic"
version_changed = "6.20.0"

@staticmethod
def _is_valid(when: str) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_log_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class NoLogPasswordsRule(AnsibleLintRule, TransformMixin):
)
severity = "LOW"
tags = ["opt-in", "security", "experimental"]
version_added = "v5.0.9"
version_changed = "5.0.9"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_prompting.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class NoPromptingRule(AnsibleLintRule):
)
tags = ["opt-in"]
severity = "VERY_LOW"
version_added = "v6.0.3"
version_changed = "6.0.3"

def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]:
"""Return matches found for a specific playbook."""
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_relative_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class RoleRelativePath(AnsibleLintRule):
description = "The ``copy`` and ``template`` modules should not use relative path for ``src``."
severity = "HIGH"
tags = ["idiom"]
version_added = "v4.0.0"
version_changed = "4.0.0"

_module_to_path_folder = {
"copy": "files",
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/rules/no_same_owner.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class NoSameOwnerRule(AnsibleLintRule):
"""
severity = "LOW"
tags = ["opt-in"]
version_changed = "6.4.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/no_tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class NoTabsRule(AnsibleLintRule):
description = "Tabs can cause unexpected display issues, use spaces"
severity = "LOW"
tags = ["formatting"]
version_added = "v4.0.0"
version_changed = "4.0.0"
allow_list = [
("lineinfile", "insertafter"),
("lineinfile", "insertbefore"),
Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/rules/only_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class OnlyBuiltinsRule(AnsibleLintRule):
severity = "MEDIUM"
description = "Check whether the playbook uses anything but ``ansible.builtin``"
tags = ["opt-in", "experimental"]
version_changed = "6.14.0"

def matchtask(
self,
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/package_latest.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class PackageIsNotLatestRule(AnsibleLintRule):
)
severity = "VERY_LOW"
tags = ["idempotency"]
version_added = "historic"
version_changed = "6.20.0"

_package_managers = [
"apk",
Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/partial_become.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class BecomeUserWithoutBecomeRule(AnsibleLintRule, TransformMixin):
description = "``become_user`` should have a corresponding ``become`` at the play or task level."
severity = "VERY_HIGH"
tags = ["unpredictability"]
version_added = "historic"
version_changed = "6.20.0"

def matchplay(
self,
Expand Down
Loading
Loading