Skip to content

Fix for galaxy with system site packages #147

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 1 commit into from
May 1, 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
26 changes: 26 additions & 0 deletions src/ansible_dev_environment/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import json
import os
import shutil
import subprocess
import sys

Expand Down Expand Up @@ -101,6 +102,31 @@
"""Return the current interpreter."""
return Path(sys.executable)

@property
def galaxy_bin(self: Config) -> Path | None:
"""Find the ansible galaxy command.

Prefer the venv over the system package over the PATH.
"""
within_venv = self.venv_bindir / "ansible-galaxy"
if within_venv.exists():
msg = f"Found ansible-galaxy in virtual environment: {within_venv}"
self._output.debug(msg)
return within_venv
system_pkg = self.site_pkg_path / "bin" / "ansible-galaxy"
if system_pkg.exists():
msg = f"Found ansible-galaxy in system packages: {system_pkg}"
self._output.debug(msg)
return system_pkg
last_resort = shutil.which("ansible-galaxy")
if last_resort:
msg = f"Found ansible-galaxy in PATH: {last_resort}"
self._output.debug(msg)
return Path(last_resort)
msg = "Failed to find ansible-galaxy."
self._output.critical(msg)
return None

Check warning on line 128 in src/ansible_dev_environment/config.py

View check run for this annotation

Codecov / codecov/patch

src/ansible_dev_environment/config.py#L128

Added line #L128 was not covered by tests

def _set_interpreter(
self: Config,
) -> None:
Expand Down
8 changes: 4 additions & 4 deletions src/ansible_dev_environment/subcommands/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def _install_galaxy_collections(
shutil.rmtree(collection.site_pkg_path)

command = (
f"{self._config.venv_bindir / 'ansible-galaxy'} collection"
f"{self._config.galaxy_bin} collection"
f" install {collections_str}"
f" -p {self._config.site_pkg_path}"
" --force"
Expand Down Expand Up @@ -191,7 +191,7 @@ def _install_galaxy_requirements(self: Installer) -> None:
shutil.rmtree(cpath)

command = (
f"{self._config.venv_bindir / 'ansible-galaxy'} collection"
f"{self._config.galaxy_bin} collection"
f" install -r {self._config.args.requirement}"
f" -p {self._config.site_pkg_path}"
" --force"
Expand Down Expand Up @@ -359,7 +359,7 @@ def _install_local_collection(

command = (
f"cd {collection.build_dir} &&"
f" {self._config.venv_bindir / 'ansible-galaxy'} collection build"
f" {self._config.galaxy_bin} collection build"
f" --output-path {collection.build_dir}"
" --force"
)
Expand Down Expand Up @@ -412,7 +412,7 @@ def _install_local_collection(
shutil.rmtree(info_dir)

command = (
f"{self._config.venv_bindir / 'ansible-galaxy'} collection"
f"{self._config.galaxy_bin} collection"
f" install {tarball} -p {self._config.site_pkg_path}"
" --force"
)
Expand Down
216 changes: 200 additions & 16 deletions tests/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,66 @@
from __future__ import annotations

import argparse
import shutil

from pathlib import Path
from typing import TYPE_CHECKING

import pytest

from ansible_dev_environment.config import Config
from ansible_dev_environment.output import Output
from ansible_dev_environment.utils import TermFeatures


if TYPE_CHECKING:
from pathlib import Path
from ansible_dev_environment.output import Output


def gen_args(
venv: str,
system_site_packages: bool = False, # noqa: FBT001, FBT002
) -> argparse.Namespace:
"""Generate the arguments.

Args:
venv: The virtual environment.
system_site_packages: Whether to include system site packages.

Returns:
The arguments.
"""
return argparse.Namespace(
verbose=0,
venv=venv,
system_site_packages=system_site_packages,
)


@pytest.mark.parametrize(
"system_site_packages",
((True, False)),
ids=["ssp_true", "ssp_false"],
)
def test_paths(tmpdir: Path, system_site_packages: bool) -> None: # noqa: FBT001
def test_paths(
tmpdir: Path,
system_site_packages: bool, # noqa: FBT001
output: Output,
) -> None:
"""Test the paths.

Several of the found directories should have a parent of the tmpdir / test_venv

Args:
tmpdir: A temporary directory.
system_site_packages: Whether to include system site packages.
output: The output fixture.
"""
venv = tmpdir / "test_venv"
args = argparse.Namespace(
args = gen_args(
venv=str(venv),
system_site_packages=system_site_packages,
verbose=0,
)
term_features = TermFeatures(color=False, links=False)

output = Output(
log_file=str(tmpdir / "test_log.log"),
log_level="debug",
log_append="false",
term_features=term_features,
verbosity=0,
)

config = Config(args=args, output=output, term_features=term_features)
config = Config(args=args, output=output, term_features=output.term_features)
config.init()

assert config.venv == venv
Expand All @@ -59,3 +74,172 @@ def test_paths(tmpdir: Path, system_site_packages: bool) -> None: # noqa: FBT00
"venv_interpreter",
):
assert venv in getattr(config, attr).parents


def test_galaxy_bin_venv(
tmpdir: Path,
monkeypatch: pytest.MonkeyPatch,
output: Output,
) -> None:
"""Test the galaxy_bin property found in venv.

Args:
tmpdir: A temporary directory.
monkeypatch: A pytest fixture for monkey patching.
output: The output fixture.
"""
venv = tmpdir / "test_venv"
args = gen_args(venv=str(venv))

config = Config(args=args, output=output, term_features=output.term_features)
config.init()

orig_exists = Path.exists
exists_called = False

def _exists(path: Path) -> bool:
if path.name != "ansible-galaxy":
return orig_exists(path)
if path.parent == config.venv_bindir:
nonlocal exists_called
exists_called = True
return True
return False

monkeypatch.setattr(Path, "exists", _exists)

assert config.galaxy_bin == venv / "bin" / "ansible-galaxy"
assert exists_called


def test_galaxy_bin_site(
tmpdir: Path,
monkeypatch: pytest.MonkeyPatch,
output: Output,
) -> None:
"""Test the galaxy_bin property found in site.

Args:
tmpdir: A temporary directory.
monkeypatch: A pytest fixture for monkey patching.
output: The output fixture.
"""
venv = tmpdir / "test_venv"
args = gen_args(venv=str(venv))

config = Config(args=args, output=output, term_features=output.term_features)
config.init()

orig_exists = Path.exists
exists_called = False

def _exists(path: Path) -> bool:
if path.name != "ansible-galaxy":
return orig_exists(path)
if path.parent == config.site_pkg_path / "bin":
nonlocal exists_called
exists_called = True
return True
return False

monkeypatch.setattr(Path, "exists", _exists)

assert config.galaxy_bin == config.site_pkg_path / "bin" / "ansible-galaxy"
assert exists_called


def test_galaxy_bin_path(
tmpdir: Path,
monkeypatch: pytest.MonkeyPatch,
output: Output,
) -> None:
"""Test the galaxy_bin property found in path.

Args:
tmpdir: A temporary directory.
monkeypatch: A pytest fixture for monkey patching.
output: The output fixture.
"""
venv = tmpdir / "test_venv"
args = gen_args(venv=str(venv))

config = Config(args=args, output=output, term_features=output.term_features)
config.init()

orig_exists = Path.exists
exists_called = False

def _exists(path: Path) -> bool:
if path.name != "ansible-galaxy":
return orig_exists(path)
nonlocal exists_called
exists_called = True
return False

monkeypatch.setattr(Path, "exists", _exists)

orig_which = shutil.which
which_called = False

def _which(name: str) -> str | None:
if not name.endswith("ansible-galaxy"):
return orig_which(name)
nonlocal which_called
which_called = True
return "patched"

monkeypatch.setattr(shutil, "which", _which)

assert config.galaxy_bin == Path("patched")
assert exists_called
assert which_called


def test_galaxy_bin_not_found(
tmpdir: Path,
monkeypatch: pytest.MonkeyPatch,
output: Output,
) -> None:
"""Test the galaxy_bin property found in venv.

Args:
tmpdir: A temporary directory.
monkeypatch: A pytest fixture for monkey patching.
output: The output fixture.
"""
venv = tmpdir / "test_venv"
args = gen_args(venv=str(venv))

config = Config(args=args, output=output, term_features=output.term_features)
config.init()

orig_exists = Path.exists
exist_called = False

def _exists(path: Path) -> bool:
if path.name == "ansible-galaxy":
nonlocal exist_called
exist_called = True
return False
return orig_exists(path)

monkeypatch.setattr(Path, "exists", _exists)

orig_which = shutil.which
which_called = False

def _which(name: str) -> str | None:
if name.endswith("ansible-galaxy"):
nonlocal which_called
which_called = True
return None
return orig_which(name)

monkeypatch.setattr(shutil, "which", _which)

with pytest.raises(SystemExit) as exc:
assert config.galaxy_bin is None

assert exc.value.code == 1
assert exist_called
assert which_called
Loading