Skip to content

Commit

Permalink
Ignore folders that are not valid namaspeces (#310)
Browse files Browse the repository at this point in the history
- Adds tests
- Fixes several regressions introduced by #297
  • Loading branch information
ssbarnea authored Aug 16, 2023
1 parent c91abe1 commit 01f0448
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 61 deletions.
119 changes: 60 additions & 59 deletions src/ansible_compat/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

import contextlib
import fnmatch
import importlib
import json
import logging
Expand Down Expand Up @@ -51,6 +50,7 @@
_logger = logging.getLogger(__name__)
# regex to extract the first version from a collection range specifier
version_re = re.compile(":[>=<]*([^,]*)")
namespace_re = re.compile("^[a-z][a-z0-9_]+$")


class AnsibleWarning(Warning):
Expand Down Expand Up @@ -575,7 +575,13 @@ def prepare_environment( # noqa: C901
for req_file in REQUIREMENT_LOCATIONS:
self.install_requirements(Path(req_file), retry=retry, offline=offline)

self._prepare_ansible_paths()

if not install_local:
return

for gpath in search_galaxy_paths(self.project_dir):
# processing all found galaxy.yml files
galaxy_path = Path(gpath)
if galaxy_path.exists():
data = yaml_from_file(galaxy_path)
Expand All @@ -591,59 +597,54 @@ def prepare_environment( # noqa: C901
destination=destination,
)

if self.cache_dir:
destination = self.cache_dir / "collections"
for name, min_version in required_collections.items():
self.install_collection(
f"{name}:>={min_version}",
destination=destination,
)

self._prepare_ansible_paths()

if not install_local:
return
if self.cache_dir:
destination = self.cache_dir / "collections"
for name, min_version in required_collections.items():
self.install_collection(
f"{name}:>={min_version}",
destination=destination,
)

if galaxy_path.exists():
if destination:
# while function can return None, that would not break the logic
colpath = Path(
f"{destination}/ansible_collections/{colpath_from_path(Path.cwd())}",
)
if colpath.is_symlink():
if os.path.realpath(colpath) == Path.cwd():
_logger.warning(
"Found symlinked collection, skipping its installation.",
)
return
if Path("galaxy.yml").exists():
if destination:
# while function can return None, that would not break the logic
colpath = Path(
f"{destination}/ansible_collections/{colpath_from_path(Path.cwd())}",
)
if colpath.is_symlink():
if os.path.realpath(colpath) == Path.cwd():
_logger.warning(
"Collection is symlinked, but not pointing to %s directory, so we will remove it.",
Path.cwd(),
"Found symlinked collection, skipping its installation.",
)
colpath.unlink()
return
_logger.warning(
"Collection is symlinked, but not pointing to %s directory, so we will remove it.",
Path.cwd(),
)
colpath.unlink()

# molecule scenario within a collection
self.install_collection_from_disk(
galaxy_path.parent,
destination=destination,
)
elif (
Path().resolve().parent.name == "roles"
and Path("../../galaxy.yml").exists()
):
# molecule scenario located within roles/<role-name>/molecule inside
# a collection
self.install_collection_from_disk(
Path("../.."),
destination=destination,
)
else:
# no collection, try to recognize and install a standalone role
self._install_galaxy_role(
self.project_dir,
role_name_check=role_name_check,
ignore_errors=True,
)
# molecule scenario within a collection
self.install_collection_from_disk(
galaxy_path.parent,
destination=destination,
)
elif (
Path().resolve().parent.name == "roles"
and Path("../../galaxy.yml").exists()
):
# molecule scenario located within roles/<role-name>/molecule inside
# a collection
self.install_collection_from_disk(
Path("../.."),
destination=destination,
)
else:
# no collection, try to recognize and install a standalone role
self._install_galaxy_role(
self.project_dir,
role_name_check=role_name_check,
ignore_errors=True,
)
# reload collections
self.load_collections()

Expand Down Expand Up @@ -894,15 +895,15 @@ def _get_galaxy_role_name(galaxy_infos: dict[str, Any]) -> str:
return galaxy_infos.get("role_name", "")


def search_galaxy_paths(search_dir: Path, depth: int = 0) -> list[str]:
def search_galaxy_paths(search_dir: Path) -> list[str]:
"""Search for galaxy paths (only one level deep)."""
galaxy_paths: list[str] = []
for file in os.listdir(search_dir):
file_path = Path(file)
if file_path.is_dir() and depth < 1:
galaxy_paths.extend(search_galaxy_paths(file_path, 1))
elif fnmatch.fnmatch(file, "galaxy.yml"):
galaxy_paths.append(str(search_dir / file))
if depth == 0 and not galaxy_paths:
return ["galaxy.yml"]
for file in [".", *os.listdir(search_dir)]:
# We ignore any folders that are not valid namespaces, just like
# ansible galaxy does at this moment.
if file != "." and not namespace_re.match(file):
continue
file_path = search_dir / file / "galaxy.yml"
if file_path.is_file():
galaxy_paths.append(str(file_path))
return galaxy_paths
Empty file.
Empty file.
27 changes: 26 additions & 1 deletion test/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
AnsibleCompatError,
InvalidPrerequisiteError,
)
from ansible_compat.runtime import CompletedProcess, Runtime
from ansible_compat.runtime import CompletedProcess, Runtime, search_galaxy_paths

if TYPE_CHECKING:
from collections.abc import Iterator
Expand Down Expand Up @@ -798,3 +798,28 @@ def test_runtime_plugins(runtime: Runtime) -> None:
assert "ansible.builtin.free" in runtime.plugins.strategy
assert "ansible.builtin.is_abs" in runtime.plugins.test
assert "ansible.builtin.bool" in runtime.plugins.filter


@pytest.mark.parametrize(
("path", "result"),
(
pytest.param(
"test/assets/galaxy_paths",
["test/assets/galaxy_paths/foo/galaxy.yml"],
id="1",
),
pytest.param(
"test/collections",
[], # should find nothing because these folders are not valid namespaces
id="2",
),
pytest.param(
"test/assets/galaxy_paths/foo",
["test/assets/galaxy_paths/foo/galaxy.yml"],
id="3",
),
),
)
def test_galaxy_path(path: str, result: list[str]) -> None:
"""Check behavior of galaxy path search."""
assert search_galaxy_paths(Path(path)) == result
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ setenv =
PIP_DISABLE_PIP_VERSION_CHECK = 1
PIP_CONSTRAINT = {toxinidir}/requirements.txt
PRE_COMMIT_COLOR = always
PYTEST_REQPASS = 82
PYTEST_REQPASS = 85
FORCE_COLOR = 1
allowlist_externals =
ansible
Expand Down

0 comments on commit 01f0448

Please sign in to comment.