Skip to content

Commit

Permalink
Allow extension of supported ansible versions
Browse files Browse the repository at this point in the history
This change will allow users to configure additional versions of
ansible that will be considered as supported.
  • Loading branch information
alisonlhart committed Jun 4, 2024
1 parent b5d027c commit 8653ab1
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 24 deletions.
4 changes: 4 additions & 0 deletions .ansible-lint
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,7 @@ task_name_prefix: "{stem} | "

# Limit the depth of the nested blocks:
# max_block_depth: 20

# Also recognize these versions of Ansible as supported:
# supported_ansible_also:
# - "2.14"
2 changes: 2 additions & 0 deletions examples/broken_supported_ansible_also/.ansible-lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Invalid supported_ansible_also type
supported_ansible_also: True
2 changes: 0 additions & 2 deletions examples/meta_runtime_version_checks/pass/meta/runtime.yml

This file was deleted.

2 changes: 2 additions & 0 deletions examples/meta_runtime_version_checks/pass_0/meta/runtime.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
requires_ansible: ">=2.15.0,<2.17.0"
2 changes: 2 additions & 0 deletions examples/meta_runtime_version_checks/pass_1/meta/runtime.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
requires_ansible: ">=2.9.10"
1 change: 1 addition & 0 deletions src/ansiblelint/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ def merge_config(file_config: dict[Any, Any], cli_config: Options) -> Options:
"enable_list": [],
"only_builtins_allow_collections": [],
"only_builtins_allow_modules": [],
"supported_ansible_also": [],
# do not include "write_list" here. See special logic below.
}

Expand Down
8 changes: 8 additions & 0 deletions src/ansiblelint/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ class Options: # pylint: disable=too-many-instance-attributes
ignore_file: Path | None = None
max_tasks: int = 100
max_block_depth: int = 20
# Refer to https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html#ansible-core-support-matrix
_default_supported = ["2.15.", "2.16.", "2.17."]
supported_ansible_also: list[str] = field(default_factory=list)

@property
def nodeps(self) -> bool:
Expand All @@ -186,6 +189,11 @@ def __post_init__(self) -> None:
if self.nodeps:
self.offline = True

@property
def supported_ansible(self) -> list[str]:
"""Returns list of ansible versions that are considered supported."""
return sorted([*self._default_supported, *self.supported_ansible_also])


options = Options()

Expand Down
31 changes: 24 additions & 7 deletions src/ansiblelint/rules/meta_runtime.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# meta-runtime

This rule checks the meta/runtime.yml `requires_ansible` key against the list of currently supported versions of ansible-core.
This rule checks the meta/runtime.yml `requires_ansible` key against the list of
currently supported versions of ansible-core.

This rule can produce messages such as:

- `meta-runtime[unsupported-version]` - `requires_ansible` key must refer to a currently supported version such as: >=2.14.0, >=2.15.0, >=2.16.0
- `meta-runtime[invalid-version]` - `requires_ansible` is not a valid requirement specification
- `meta-runtime[unsupported-version]` - `requires_ansible` key must refer to a
currently supported version such as: >=2.14.0, >=2.15.0, >=2.16.0
- `meta-runtime[invalid-version]` - `requires_ansible` is not a valid
requirement specification

Please note that the linter will allow only a full version of Ansible such `2.16.0` and not allow their short form, like `2.16`. This is a safety measure
for asking authors to mention an explicit version that they tested with. Over the years we spotted multiple problems caused by the use of the short versions, users
ended up trying an outdated version that was never tested against by the collection maintainer.
Please note that the linter will allow only a full version of Ansible such
`2.16.0` and not allow their short form, like `2.16`. This is a safety measure
for asking authors to mention an explicit version that they tested with. Over
the years we spotted multiple problems caused by the use of the short versions,
users ended up trying an outdated version that was never tested against by the
collection maintainer.

## Problematic code

Expand All @@ -19,7 +25,6 @@ ended up trying an outdated version that was never tested against by the collect
requires_ansible: ">=2.9"
```
```yaml
# runtime.yml
---
Expand All @@ -33,3 +38,15 @@ requires_ansible: "2.15"
---
requires_ansible: ">=2.15.0"
```
## Configuration
In addition to the internal list of supported Ansible versions, users can
configure additional values. This allows those that want to maintain content
that requires a version of ansible-core that is already out of support.
```yaml
# Also recognize these versions of Ansible as supported:
supported_ansible_also:
- "2.14"
```
51 changes: 36 additions & 15 deletions src/ansiblelint/rules/meta_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,8 @@ class CheckRequiresAnsibleVersion(AnsibleLintRule):
tags = ["metadata"]
version_added = "v6.11.0 (last update)"

# Refer to https://access.redhat.com/support/policy/updates/ansible-automation-platform
# Also add devel to this list
supported_ansible = ["2.15.", "2.16.", "2.17."]
supported_ansible_examples = [f">={x}0" for x in supported_ansible]
_ids = {
"meta-runtime[unsupported-version]": f"'requires_ansible' key must refer to a currently supported version such as: {', '.join(supported_ansible_examples)}",
"meta-runtime[unsupported-version]": "'requires_ansible' key must refer to a currently supported version",
"meta-runtime[invalid-version]": "'requires_ansible' is not a valid requirement specification",
}

Expand All @@ -50,22 +46,26 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
if file.kind != "meta-runtime":
return []

version_required = file.data.get("requires_ansible", None)
requires_ansible = file.data.get("requires_ansible", None)

if version_required:
if not any(
version in version_required for version in self.supported_ansible
if requires_ansible:
if self.options and not any(
version in requires_ansible
for version in self.options.supported_ansible
):
supported_ansible = [f">={x}0" for x in self.options.supported_ansible]
msg = f"'requires_ansible' key must refer to a currently supported version such as: {', '.join(supported_ansible)}"

results.append(
self.create_matcherror(
message=self._ids["meta-runtime[unsupported-version]"],
message=msg,
tag="meta-runtime[unsupported-version]",
filename=file,
),
)

try:
SpecifierSet(version_required)
SpecifierSet(requires_ansible)
except ValueError:
results.append(
self.create_matcherror(
Expand All @@ -90,10 +90,10 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
("test_file", "failures", "tags"),
(
pytest.param(
"examples/meta_runtime_version_checks/pass/meta/runtime.yml",
"examples/meta_runtime_version_checks/pass_0/meta/runtime.yml",
0,
"meta-runtime[unsupported-version]",
id="pass",
id="pass0",
),
pytest.param(
"examples/meta_runtime_version_checks/fail_0/meta/runtime.yml",
Expand All @@ -115,16 +115,37 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
),
),
)
def test_meta_supported_version(
def test_default_meta_supported_version(
default_rules_collection: RulesCollection,
test_file: str,
failures: int,
tags: str,
) -> None:
"""Test rule matches."""
"""Test for default supported ansible versions."""
default_rules_collection.register(CheckRequiresAnsibleVersion())
results = Runner(test_file, rules=default_rules_collection).run()
for result in results:
assert result.rule.id == CheckRequiresAnsibleVersion().id
assert result.tag == tags
assert len(results) == failures

@pytest.mark.parametrize(
("test_file", "failures"),
(
pytest.param(
"examples/meta_runtime_version_checks/pass_1/meta/runtime.yml",
0,
id="pass1",
),
),
)
def test_added_meta_supported_version(
default_rules_collection: RulesCollection,
test_file: str,
failures: int,
) -> None:
"""Test for added supported ansible versions in the config."""
default_rules_collection.register(CheckRequiresAnsibleVersion())
default_rules_collection.options.supported_ansible_also = ["2.9"]
results = Runner(test_file, rules=default_rules_collection).run()
assert len(results) == failures
8 changes: 8 additions & 0 deletions src/ansiblelint/rules/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
],
id="ansible-lint-config-broken",
),
pytest.param(
"examples/broken_supported_ansible_also/.ansible-lint",
"ansible-lint-config",
[
r".*supported_ansible_also True is not of type 'array'.*https://",
],
id="ansible-lint-config-broken",
),
pytest.param(
"examples/ansible-navigator.yml",
"ansible-navigator-config",
Expand Down
7 changes: 7 additions & 0 deletions src/ansiblelint/schemas/ansible-lint-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,13 @@
"title": "Strict",
"type": "boolean"
},
"supported_ansible_also": {
"items": {
"type": "string"
},
"title": "Add supported ansible versions",
"type": "array"
},
"tags": {
"items": {
"type": "string"
Expand Down

0 comments on commit 8653ab1

Please sign in to comment.