Skip to content

Commit eb771f5

Browse files
Avoid galaxy install failure with already symlinked collections (#166)
* Avoid galaxy install failure with already symlinked collections Fixes: #165 * chore: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent a986b49 commit eb771f5

File tree

3 files changed

+48
-11
lines changed

3 files changed

+48
-11
lines changed

src/ansible_compat/loaders.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
11
"""Utilities for loading various files."""
2+
from __future__ import annotations
3+
4+
import os
25
from typing import Any
36

47
import yaml
58

9+
from ansible_compat.errors import InvalidPrerequisiteError
10+
611

712
def yaml_from_file(filepath: str) -> Any:
813
"""Return a loaded YAML file."""
914
with open(filepath, encoding="utf-8") as content:
1015
return yaml.load(content, Loader=yaml.FullLoader)
16+
17+
18+
def colpath_from_path(filepath: str) -> str | None:
19+
"""Return a FQCN from a path."""
20+
galaxy_file = f"{filepath}/galaxy.yml"
21+
if os.path.exists(galaxy_file):
22+
galaxy = yaml_from_file(galaxy_file)
23+
for k in ("namespace", "name"):
24+
if k not in galaxy:
25+
raise InvalidPrerequisiteError(
26+
f"{galaxy_file} is missing the following mandatory field {k}"
27+
)
28+
return f"{galaxy['namespace']}/{galaxy['name']}"
29+
return None

src/ansible_compat/runtime.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
InvalidPrerequisiteError,
2727
MissingAnsibleError,
2828
)
29-
from ansible_compat.loaders import yaml_from_file
29+
from ansible_compat.loaders import colpath_from_path, yaml_from_file
3030
from ansible_compat.prerun import get_cache_dir
3131

3232
if TYPE_CHECKING:
@@ -315,7 +315,7 @@ def install_requirements(self, requirement: str, retry: bool = False) -> None:
315315
_logger.error(run.stdout)
316316
raise AnsibleCommandError(run)
317317

318-
def prepare_environment(
318+
def prepare_environment( # noqa: C901
319319
self,
320320
required_collections: Optional[Dict[str, str]] = None,
321321
retry: bool = False,
@@ -352,6 +352,21 @@ def prepare_environment(
352352
return
353353

354354
if os.path.exists("galaxy.yml"):
355+
if destination:
356+
# while function can return None, that would not break the logic
357+
colpath = f"{destination}/ansible_collections/{colpath_from_path(os.getcwd())}"
358+
if os.path.islink(colpath):
359+
if os.path.realpath(colpath) == os.getcwd():
360+
_logger.warning(
361+
"Found symlinked collection, skipping its installation."
362+
)
363+
return
364+
_logger.warning(
365+
"Collection is symlinked, but not pointing to %s directory, so we will remove it.",
366+
os.getcwd(),
367+
)
368+
os.unlink(colpath)
369+
355370
# molecule scenario within a collection
356371
self.install_collection_from_disk(".", destination=destination)
357372
elif pathlib.Path().resolve().parent.name == "roles" and os.path.exists(

test/test_runtime.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -643,16 +643,19 @@ def test_install_collection_from_disk_fail() -> None:
643643
"""Tests that we fail to install a broken collection."""
644644
with remember_cwd("test/collections/acme.broken"):
645645
runtime = Runtime(isolated=True)
646-
exception: Type[Exception]
647-
if runtime.version_in_range(upper="2.11"):
648-
exception = AnsibleCommandError
649-
msg = "Got 1 exit code while running: ansible-galaxy collection build"
650-
else:
651-
exception = InvalidPrerequisiteError
652-
msg = "is missing the following mandatory"
653-
with pytest.raises(exception, match=msg):
654-
# this should call install_collection_from_disk(".")
646+
with pytest.raises(RuntimeError) as exc_info:
655647
runtime.prepare_environment(install_local=True)
648+
# based on version of Ansible used, we might get a different error,
649+
# but both errors should be considered acceptable
650+
assert exc_info.type in (
651+
RuntimeError,
652+
AnsibleCompatError,
653+
AnsibleCommandError,
654+
InvalidPrerequisiteError,
655+
)
656+
assert exc_info.match(
657+
"(is missing the following mandatory|Got 1 exit code while running: ansible-galaxy collection build)"
658+
)
656659

657660

658661
def test_prepare_environment_offline_role() -> None:

0 commit comments

Comments
 (0)