diff options
author | Sloane Hertel <19572925+s-hertel@users.noreply.github.com> | 2022-04-13 12:21:55 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-13 11:21:55 -0500 |
commit | 8bbcfebd5c7e5efeb9c8dc173a3fa5ebceab3df5 (patch) | |
tree | 5dcde28e76aaad2025b7b4f95c6e113b2c2fa2c3 | |
parent | efbe050c29d11153253724d83c77ce343ff67c3f (diff) | |
download | ansible-8bbcfebd5c7e5efeb9c8dc173a3fa5ebceab3df5.tar.gz |
Get git executable for collections in git repos (#77493) (#77501)
* Fix traceback installing collections from git repos if git is not installed
(cherry picked from commit 477c55b0d231dcb62cfcec21fe93a8fee95b4215)
3 files changed, 39 insertions, 7 deletions
diff --git a/changelogs/fragments/77493-ansible-galaxy-find-git-executable-before-using.yaml b/changelogs/fragments/77493-ansible-galaxy-find-git-executable-before-using.yaml new file mode 100644 index 0000000000..835bd6a926 --- /dev/null +++ b/changelogs/fragments/77493-ansible-galaxy-find-git-executable-before-using.yaml @@ -0,0 +1,2 @@ +bugfixes: + - Fix traceback when installing a collection from a git repository and git is not installed (https://github.com/ansible/ansible/issues/77479). diff --git a/lib/ansible/galaxy/collection/concrete_artifact_manager.py b/lib/ansible/galaxy/collection/concrete_artifact_manager.py index 73b392f45c..5be1eaabc9 100644 --- a/lib/ansible/galaxy/collection/concrete_artifact_manager.py +++ b/lib/ansible/galaxy/collection/concrete_artifact_manager.py @@ -38,6 +38,7 @@ from ansible.galaxy import get_collections_galaxy_meta_info from ansible.galaxy.dependency_resolution.dataclasses import _GALAXY_YAML from ansible.galaxy.user_agent import user_agent from ansible.module_utils._text import to_bytes, to_native, to_text +from ansible.module_utils.common.process import get_bin_path from ansible.module_utils.common.yaml import yaml_load from ansible.module_utils.six.moves.urllib.error import URLError from ansible.module_utils.six.moves.urllib.parse import urldefrag @@ -350,11 +351,19 @@ def _extract_collection_from_git(repo_url, coll_ver, b_path): prefix=to_bytes(name, errors='surrogate_or_strict'), ) # type: bytes + try: + git_executable = get_bin_path('git') + except ValueError as err: + raise AnsibleError( + "Could not find git executable to extract the collection from the Git repository `{repo_url!s}`.". + format(repo_url=to_native(git_url)) + ) from err + # Perform a shallow clone if simply cloning HEAD if version == 'HEAD': - git_clone_cmd = 'git', 'clone', '--depth=1', git_url, to_text(b_checkout_path) + git_clone_cmd = git_executable, 'clone', '--depth=1', git_url, to_text(b_checkout_path) else: - git_clone_cmd = 'git', 'clone', git_url, to_text(b_checkout_path) + git_clone_cmd = git_executable, 'clone', git_url, to_text(b_checkout_path) # FIXME: '--branch', version try: @@ -368,7 +377,7 @@ def _extract_collection_from_git(repo_url, coll_ver, b_path): proc_err, ) - git_switch_cmd = 'git', 'checkout', to_text(version) + git_switch_cmd = git_executable, 'checkout', to_text(version) try: subprocess.check_call(git_switch_cmd, cwd=b_checkout_path) except subprocess.CalledProcessError as proc_err: diff --git a/test/units/galaxy/test_collection_install.py b/test/units/galaxy/test_collection_install.py index d4565fd59d..00e082d7cd 100644 --- a/test/units/galaxy/test_collection_install.py +++ b/test/units/galaxy/test_collection_install.py @@ -18,6 +18,7 @@ import yaml from io import BytesIO, StringIO from units.compat.mock import MagicMock +from unittest import mock import ansible.module_utils.six.moves.urllib.error as urllib_error @@ -27,6 +28,7 @@ from ansible.errors import AnsibleError from ansible.galaxy import collection, api, dependency_resolution from ansible.galaxy.dependency_resolution.dataclasses import Candidate, Requirement from ansible.module_utils._text import to_bytes, to_native, to_text +from ansible.module_utils.common.process import get_bin_path from ansible.utils import context_objects as co from ansible.utils.display import Display @@ -171,6 +173,22 @@ def galaxy_server(): return galaxy_api +def test_concrete_artifact_manager_scm_no_executable(monkeypatch): + url = 'https://github.com/org/repo' + version = 'commitish' + mock_subprocess_check_call = MagicMock() + monkeypatch.setattr(collection.concrete_artifact_manager.subprocess, 'check_call', mock_subprocess_check_call) + mock_mkdtemp = MagicMock(return_value='') + monkeypatch.setattr(collection.concrete_artifact_manager, 'mkdtemp', mock_mkdtemp) + + error = re.escape( + "Could not find git executable to extract the collection from the Git repository `https://github.com/org/repo`" + ) + with mock.patch.dict(os.environ, {"PATH": ""}): + with pytest.raises(AnsibleError, match=error): + collection.concrete_artifact_manager._extract_collection_from_git(url, version, b'path') + + @pytest.mark.parametrize( 'url,version,trailing_slash', [ @@ -193,10 +211,12 @@ def test_concrete_artifact_manager_scm_cmd(url, version, trailing_slash, monkeyp repo = 'https://github.com/org/repo' if trailing_slash: repo += '/' - clone_cmd = ('git', 'clone', repo, '') + + git_executable = get_bin_path('git') + clone_cmd = (git_executable, 'clone', repo, '') assert mock_subprocess_check_call.call_args_list[0].args[0] == clone_cmd - assert mock_subprocess_check_call.call_args_list[1].args[0] == ('git', 'checkout', 'commitish') + assert mock_subprocess_check_call.call_args_list[1].args[0] == (git_executable, 'checkout', 'commitish') @pytest.mark.parametrize( @@ -222,10 +242,11 @@ def test_concrete_artifact_manager_scm_cmd_shallow(url, version, trailing_slash, repo = 'https://github.com/org/repo' if trailing_slash: repo += '/' - shallow_clone_cmd = ('git', 'clone', '--depth=1', repo, '') + git_executable = get_bin_path('git') + shallow_clone_cmd = (git_executable, 'clone', '--depth=1', repo, '') assert mock_subprocess_check_call.call_args_list[0].args[0] == shallow_clone_cmd - assert mock_subprocess_check_call.call_args_list[1].args[0] == ('git', 'checkout', 'HEAD') + assert mock_subprocess_check_call.call_args_list[1].args[0] == (git_executable, 'checkout', 'HEAD') def test_build_requirement_from_path(collection_artifact): |