Skip to content

Add rule to verify that galaxy.yml collection version is present and >= 1.0.0 #2383

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

Merged
merged 6 commits into from
Sep 16, 2022
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
2 changes: 1 addition & 1 deletion .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ jobs:
WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:TOX_PARALLEL_NO_SPINNER
# Number of expected test passes, safety measure for accidental skip of
# tests. Update value if you add/remove tests.
PYTEST_REQPASS: 703
PYTEST_REQPASS: 705

steps:
- name: Activate WSL1
Expand Down
14 changes: 14 additions & 0 deletions examples/meta/galaxy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
name: foo
namespace: bar
version: 0.2.3 # <-- that version is not valid, should be 1.0.0 or greater
authors:
- John
readme: ../README.md
description: "..."
dependencies:
other_namespace.collection1: ">=1.0.0"
other_namespace.collection2: ">=2.0.0,<3.0.0"
anderson55.my_collection: "*" # note: "*" selects the highest version available
license:
- Apache-2.0
39 changes: 39 additions & 0 deletions src/ansiblelint/rules/galaxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# galaxy

This rule identifies if the collection version mentioned in galaxy.yml is ideal in terms of the version number being greater than or equal to `1.0.0`.

This rule can produce messages such:

- `galaxy[version-missing]` - `galaxy.yaml` should have version tag.
- `galaxy[version-incorrect]` - collection version should be greater than or equal to `1.0.0`

If you want to ignore some of the messages above, you can add any of them to
the `ignore_list`.

## Problematic code

```yaml
# galaxy.yml
---
name: foo
namespace: bar
version: 0.2.3 # <-- collection version should be >= 1.0.0
authors:
- John
readme: ../README.md
description: "..."
```

## Correct code

```yaml
# galaxy.yml
---
name: foo
namespace: bar
version: 1.0.0
authors:
- John
readme: ../README.md
description: "..."
```
108 changes: 108 additions & 0 deletions src/ansiblelint/rules/galaxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Implementation of GalaxyRule."""
from __future__ import annotations

import sys
from functools import total_ordering
from typing import TYPE_CHECKING, Any

from ansiblelint.errors import MatchError
from ansiblelint.rules import AnsibleLintRule
from ansiblelint.utils import LINE_NUMBER_KEY

if TYPE_CHECKING:
from typing import Optional

from ansiblelint.constants import odict
from ansiblelint.file_utils import Lintable


class GalaxyRule(AnsibleLintRule):
"""Rule for checking collection version is greater than 1.0.0."""

id = "galaxy"
description = "Confirm via galaxy.yml file if collection version is greater than or equal to 1.0.0"
severity = "MEDIUM"
tags = ["metadata"]
version_added = "v6.5.0 (last update)"

def matchplay(self, file: Lintable, data: odict[str, Any]) -> list[MatchError]:
"""Return matches found for a specific play (entry in playbook)."""
if file.kind != "galaxy": # type: ignore
return []
if "version" not in data:
return [
self.create_matcherror(
message="galaxy.yaml should have version tag.",
linenumber=data[LINE_NUMBER_KEY],
tag="galaxy[version-missing]",
filename=file,
)
]
if Version(data.get("version")) < Version("1.0.0"):
return [
self.create_matcherror(
message="collection version should be greater than or equal to 1.0.0",
linenumber=data[LINE_NUMBER_KEY],
tag="galaxy[version-incorrect]",
filename=file,
)
]
return []


@total_ordering
class Version:
"""Simple class to compare arbitrary versions."""

def __init__(self, version_string: str):
"""Construct a Version object."""
self.components = version_string.split(".")

def __eq__(self, other: object) -> bool:
"""Implement equality comparison."""
other = _coerce(other)
if not isinstance(other, Version):
return NotImplemented

return self.components == other.components

def __lt__(self, other: Version) -> bool:
"""Implement lower-than operation."""
other = _coerce(other)
if not isinstance(other, Version):
return NotImplemented

return self.components < other.components


def _coerce(other: object) -> Version:
if isinstance(other, str):
other = Version(other)
if isinstance(other, (int, float)):
other = Version(str(other))
if isinstance(other, Version):
return other
raise NotImplementedError(f"Unable to coerce object type {type(other)} to Version")


if "pytest" in sys.modules: # noqa: C901

from ansiblelint.rules import RulesCollection
from ansiblelint.runner import Runner

def test_galaxy_collection_version_positive() -> None:
"""Positive test for collection version in galaxy."""
collection = RulesCollection()
collection.register(GalaxyRule())
success = "examples/galaxy.yml"
good_runner = Runner(success, rules=collection)
assert [] == good_runner.run()

def test_galaxy_collection_version_negative() -> None:
"""Negative test for collection version in galaxy."""
collection = RulesCollection()
collection.register(GalaxyRule())
failure = "examples/meta/galaxy.yml"
bad_runner = Runner(failure, rules=collection)
errs = bad_runner.run()
assert len(errs) == 1
1 change: 1 addition & 0 deletions src/ansiblelint/schemas/ansible-lint-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"empty-string-compare",
"fqcn-builtins",
"latest",
"galaxy-collection-version",
"ignore-errors",
"inline-env-var",
"internal-error",
Expand Down
4 changes: 2 additions & 2 deletions test/test_rules_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,5 @@ def test_rules_id_format() -> None:
rule.help != "" or rule.description or rule.__doc__
), f"Rule {rule.id} must have at least one of: .help, .description, .__doc__"
assert "yaml" in keys, "yaml rule is missing"
assert len(rules) == 41 # update this number when adding new rules!
assert len(keys) == 41, "Duplicate rule ids?"
assert len(rules) == 42 # update this number when adding new rules!
assert len(keys) == 42, "Duplicate rule ids?"