diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1fed062979b..ec26ceb18a0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -89,6 +89,9 @@ jobs: run: poetry run mypy - name: Run pytest (integration suite) + env: + POETRY_TEST_INTEGRATION_GIT_USERNAME: ${GITHUB_ACTOR} + POETRY_TEST_INTEGRATION_GIT_PASSWORD: ${{ secrets.GITHUB_TOKEN }} run: poetry run python -m pytest -p no:sugar -q --integration tests/integration - name: Get Plugin Version (poetry-plugin-export) diff --git a/src/poetry/repositories/http.py b/src/poetry/repositories/http.py index 02d8210c46c..787ae847f2b 100644 --- a/src/poetry/repositories/http.py +++ b/src/poetry/repositories/http.py @@ -17,7 +17,6 @@ from poetry.core.packages.utils.link import Link from poetry.core.version.markers import parse_marker -from poetry.config.config import Config from poetry.repositories.cached import CachedRepository from poetry.repositories.exceptions import PackageNotFound from poetry.repositories.exceptions import RepositoryError @@ -29,6 +28,7 @@ if TYPE_CHECKING: + from poetry.config.config import Config from poetry.inspection.info import PackageInfo @@ -43,7 +43,7 @@ def __init__( super().__init__(name, disable_cache) self._url = url self._authenticator = Authenticator( - config=config or Config(use_environment=True), + config=config, cache_id=name, disable_cache=disable_cache, ) diff --git a/src/poetry/utils/authenticator.py b/src/poetry/utils/authenticator.py index 01af47887eb..87768ad89c9 100644 --- a/src/poetry/utils/authenticator.py +++ b/src/poetry/utils/authenticator.py @@ -18,6 +18,7 @@ from cachecontrol import CacheControl from cachecontrol.caches import FileCache +from poetry.config.config import Config from poetry.exceptions import PoetryException from poetry.locations import REPOSITORY_CACHE_DIR from poetry.utils.helpers import get_cert @@ -31,8 +32,6 @@ from cleo.io.io import IO - from poetry.config.config import Config - logger = logging.getLogger(__name__) @@ -84,12 +83,12 @@ def get_http_credentials( class Authenticator: def __init__( self, - config: Config, + config: Config | None = None, io: IO | None = None, cache_id: str | None = None, disable_cache: bool = False, ) -> None: - self._config = config + self._config = config or Config(use_environment=True) self._io = io self._sessions_for_netloc: dict[str, requests.Session] = {} self._credentials: dict[str, HTTPAuthCredential] = {} @@ -371,3 +370,15 @@ def _get_certs_for_url(self, url: str) -> dict[str, Path | None]: if selected: return selected.certs(config=self._config) return {"cert": None, "verify": None} + + +_authenticator: Authenticator | None = None + + +def get_default_authenticator() -> Authenticator: + global _authenticator + + if _authenticator is None: + _authenticator = Authenticator() + + return _authenticator diff --git a/src/poetry/vcs/git/backend.py b/src/poetry/vcs/git/backend.py index 4c7592344c4..13ea334e8d2 100644 --- a/src/poetry/vcs/git/backend.py +++ b/src/poetry/vcs/git/backend.py @@ -18,6 +18,7 @@ from dulwich.repo import Repo from poetry.console.exceptions import PoetrySimpleConsoleException +from poetry.utils.authenticator import get_default_authenticator from poetry.utils.helpers import remove_directory @@ -181,7 +182,11 @@ def _fetch_remote_refs(cls, url: str, local: Repo) -> FetchPackResult: """ client: GitClient path: str - client, path = get_transport_and_path(url) + + credentials = get_default_authenticator().get_credentials_for_url(url=url) + client, path = get_transport_and_path( + url, username=credentials.username, password=credentials.password + ) with local: return client.fetch( diff --git a/tests/integration/test_utils_vcs_git.py b/tests/integration/test_utils_vcs_git.py index 306875f91b8..d705d168e18 100644 --- a/tests/integration/test_utils_vcs_git.py +++ b/tests/integration/test_utils_vcs_git.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import uuid from copy import deepcopy @@ -15,6 +16,7 @@ from poetry.core.pyproject.toml import PyProjectTOML from poetry.console.exceptions import PoetrySimpleConsoleException +from poetry.utils.authenticator import Authenticator from poetry.vcs.git import Git from poetry.vcs.git.backend import GitRefSpec @@ -249,6 +251,53 @@ def test_system_git_fallback_on_http_401( spy.assert_called_once() +GIT_USERNAME = os.environ.get("POETRY_TEST_INTEGRATION_GIT_USERNAME") +GIT_PASSWORD = os.environ.get("POETRY_TEST_INTEGRATION_GIT_PASSWORD") +HTTP_AUTH_CREDENTIALS_AVAILABLE = not (GIT_USERNAME and GIT_PASSWORD) + + +@pytest.mark.skipif( + HTTP_AUTH_CREDENTIALS_AVAILABLE, + reason="HTTP authentication credentials not available", +) +def test_configured_repository_http_auth( + mocker: MockerFixture, source_url: str, config: Config +) -> None: + from poetry.vcs.git import backend + + spy_clone_legacy = mocker.spy(Git, "_clone_legacy") + spy_get_transport_and_path = mocker.spy(backend, "get_transport_and_path") + + config.merge( + { + "repositories": {"git-repo": {"url": source_url}}, + "http-basic": { + "git-repo": { + "username": GIT_USERNAME, + "password": GIT_PASSWORD, + } + }, + } + ) + + mocker.patch( + "poetry.vcs.git.backend.get_default_authenticator", + return_value=Authenticator(config=config), + ) + + with Git.clone(url=source_url, branch="0.1") as repo: + assert_version(repo, BRANCH_TO_REVISION_MAP["0.1"]) + + spy_clone_legacy.assert_not_called() + + spy_get_transport_and_path.assert_called_with( + location=source_url, + username=GIT_USERNAME, + password=GIT_PASSWORD, + ) + spy_get_transport_and_path.assert_called_once() + + def test_system_git_called_when_configured( mocker: MockerFixture, source_url: str, use_system_git_client: None ) -> None: