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

Adding logic for configuring supported ansible versions #4203

Merged
merged 1 commit into from
Jun 4, 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
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: 1 addition & 1 deletion .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
env:
# Number of expected test passes, safety measure for accidental skip of
# tests. Update value if you add/remove tests.
PYTEST_REQPASS: 881
PYTEST_REQPASS: 882
steps:
- uses: actions/checkout@v4
with:
Expand Down
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
requires_ansible: ">=2.15.0,<2.17.0"
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
Loading