Skip to content

tests(config-variations): Git schemes in repo URLs #426

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions src/vcspull/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
from logging import NullHandler

from . import cli
from .url import enable_ssh_style_url_detection # Import custom URL handling

logging.getLogger(__name__).addHandler(NullHandler())
3 changes: 3 additions & 0 deletions src/vcspull/cli/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from vcspull import exc
from vcspull.config import filter_repos, find_config_files, load_configs
from vcspull.url import enable_ssh_style_url_detection

if t.TYPE_CHECKING:
import argparse
Expand Down Expand Up @@ -147,6 +148,8 @@ def update_repo(
# repo_dict: Dict[str, Union[str, Dict[str, GitRemote], pathlib.Path]]
) -> GitSync:
"""Synchronize a single repository."""
# Ensure SSH-style URLs are recognized as explicit Git URLs
enable_ssh_style_url_detection()
repo_dict = deepcopy(repo_dict)
if "pip_url" not in repo_dict:
repo_dict["pip_url"] = repo_dict.pop("url")
Expand Down
113 changes: 113 additions & 0 deletions src/vcspull/url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""URL handling for vcspull."""

from __future__ import annotations

from typing import Any

from libvcs.url.git import DEFAULT_RULES

_orig_rule_meta: dict[str, tuple[bool, int]] = {}


def enable_ssh_style_url_detection() -> None:
"""Enable detection of SSH-style URLs as explicit Git URLs.

This makes the core-git-scp rule explicit, which allows URLs like
'user@hostname:path/to/repo.git' to be detected with is_explicit=True.

Examples
--------
>>> from vcspull.url import enable_ssh_style_url_detection
>>> from libvcs.url.git import GitURL
>>> # Without the patch
>>> GitURL.is_valid('user@hostname:path/to/repo.git', is_explicit=True)
False
>>> # With the patch
>>> enable_ssh_style_url_detection()
>>> GitURL.is_valid('user@hostname:path/to/repo.git', is_explicit=True)
True
"""
# Patch the core-git-scp rule, storing its original state if not already stored
for rule in DEFAULT_RULES:
if rule.label == "core-git-scp":
if rule.label not in _orig_rule_meta:
_orig_rule_meta[rule.label] = (rule.is_explicit, rule.weight)
rule.is_explicit = True
rule.weight = 100
break


def disable_ssh_style_url_detection() -> None:
"""Disable detection of SSH-style URLs as explicit Git URLs.

This reverts the core-git-scp rule to its original state, where URLs like
'user@hostname:path/to/repo.git' are not detected with is_explicit=True.

Examples
--------
>>> from vcspull.url import enable_ssh_style_url_detection
>>> from vcspull.url import disable_ssh_style_url_detection
>>> from libvcs.url.git import GitURL
>>> # Enable the patch
>>> enable_ssh_style_url_detection()
>>> GitURL.is_valid('user@hostname:path/to/repo.git', is_explicit=True)
True
>>> # Disable the patch
>>> disable_ssh_style_url_detection()
>>> GitURL.is_valid('user@hostname:path/to/repo.git', is_explicit=True)
False
"""
# Restore the core-git-scp rule to its original state, if known
for rule in DEFAULT_RULES:
if rule.label == "core-git-scp":
orig = _orig_rule_meta.get(rule.label)
if orig:
rule.is_explicit, rule.weight = orig
_orig_rule_meta.pop(rule.label, None)
else:
# Fallback to safe defaults
rule.is_explicit = False
rule.weight = 0
break


def is_ssh_style_url_detection_enabled() -> bool:
"""Check if SSH-style URL detection is enabled.

Returns
-------
bool: True if SSH-style URL detection is enabled, False otherwise.
"""
for rule in DEFAULT_RULES:
if rule.label == "core-git-scp":
return rule.is_explicit
return False


"""
Context manager and utility for SSH-style URL detection.
"""


class ssh_style_url_detection:
"""Context manager to enable/disable SSH-style URL detection."""

def __init__(self, enabled: bool = True) -> None:
self.enabled = enabled

def __enter__(self) -> None:
"""Enable or disable SSH-style URL detection on context enter."""
if self.enabled:
enable_ssh_style_url_detection()
else:
disable_ssh_style_url_detection()

def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: Any,
) -> None:
"""Restore original SSH-style URL detection state on context exit."""
# Always restore to disabled after context
disable_ssh_style_url_detection()
29 changes: 29 additions & 0 deletions tests/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,35 @@ class ConfigVariationTest(t.NamedTuple):
""",
remote_list=["git_scheme_repo"],
),
ConfigVariationTest(
test_id="expanded_repo_style_with_unprefixed_remote_3",
config_tpl="""
{tmp_path}/study/myrepo:
{CLONE_NAME}:
repo: git+file://{path}
remotes:
git_scheme_repo: [email protected]:org/repo.git
""",
remote_list=["git_scheme_repo"],
),
ConfigVariationTest(
test_id="expanded_repo_style_with_unprefixed_repo",
config_tpl="""
{tmp_path}/study/myrepo:
{CLONE_NAME}:
repo: [email protected]:org/repo.git
""",
remote_list=["git_scheme_repo"],
),
ConfigVariationTest(
test_id="expanded_repo_style_with_prefixed_repo_3_with_prefix",
config_tpl="""
{tmp_path}/study/myrepo:
{CLONE_NAME}:
repo: git+ssh://[email protected]:org/repo.git
""",
remote_list=["git_scheme_repo"],
),
]


Expand Down
141 changes: 141 additions & 0 deletions tests/test_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""Tests for URL handling in vcspull."""

from __future__ import annotations

import pytest
from libvcs.url.git import DEFAULT_RULES, GitURL

from vcspull.url import (
disable_ssh_style_url_detection,
enable_ssh_style_url_detection,
ssh_style_url_detection,
)


def test_ssh_style_url_detection_toggle() -> None:
"""Test that SSH-style URL detection can be toggled on and off."""
url = "[email protected]:org/repo.git"

# First, disable the detection
disable_ssh_style_url_detection()

# Without the patch, SSH-style URLs should not be detected as explicit
assert GitURL.is_valid(url) # Should be valid in non-explicit mode
assert not GitURL.is_valid(
url, is_explicit=True
) # Should not be valid in explicit mode

# Now enable the detection
enable_ssh_style_url_detection()

# With the patch, SSH-style URLs should be detected as explicit
assert GitURL.is_valid(url) # Should still be valid in non-explicit mode
assert GitURL.is_valid(
url, is_explicit=True
) # Should now be valid in explicit mode

# Verify the rule used
git_url = GitURL(url)
assert git_url.rule == "core-git-scp"

# Re-enable for other tests
enable_ssh_style_url_detection()


@pytest.mark.parametrize(
"url",
[
"[email protected]:org/repo.git",
"[email protected]:vcs-python/vcspull.git",
"[email protected]:vcs-python/vcspull.git",
"[email protected]:path/to/repo.git",
],
)
def test_ssh_style_url_detection(url: str) -> None:
"""Test that SSH-style URLs are correctly detected."""
# Ensure detection is enabled
enable_ssh_style_url_detection()

assert GitURL.is_valid(url)
assert GitURL.is_valid(url, is_explicit=True) # Should be valid in explicit mode
git_url = GitURL(url)
assert git_url.rule == "core-git-scp"


@pytest.mark.parametrize(
"url,expected_user,expected_hostname,expected_path",
[
(
"[email protected]:org/repo.git",
"user",
"myhostname.de",
"org/repo",
),
(
"[email protected]:vcs-python/vcspull.git",
"git",
"github.com",
"vcs-python/vcspull",
),
(
"[email protected]:vcs-python/vcspull.git",
"git",
"gitlab.com",
"vcs-python/vcspull",
),
(
"[email protected]:path/to/repo.git",
"user",
"custom-host.com",
"path/to/repo",
),
],
)
def test_ssh_style_url_parsing(
url: str, expected_user: str, expected_hostname: str, expected_path: str
) -> None:
"""Test that SSH-style URLs are correctly parsed."""
# Ensure detection is enabled
enable_ssh_style_url_detection()

git_url = GitURL(url)
assert git_url.user == expected_user
assert git_url.hostname == expected_hostname
assert git_url.path == expected_path
assert git_url.suffix == ".git"


def test_enable_disable_restores_original_state() -> None:
"""Original rule metadata is preserved and restored after enable/disable."""
# Ensure any prior patch is cleared
disable_ssh_style_url_detection()
# Find the core-git-scp rule and capture its original state
rule = next(r for r in DEFAULT_RULES if r.label == "core-git-scp")
orig_state = (rule.is_explicit, rule.weight)

# Disabling without prior enable should leave original state
disable_ssh_style_url_detection()
assert (rule.is_explicit, rule.weight) == orig_state

# Enable should patch
enable_ssh_style_url_detection()
assert rule.is_explicit is True
assert rule.weight == 100

# Disable should restore to original
disable_ssh_style_url_detection()
assert (rule.is_explicit, rule.weight) == orig_state


def test_context_manager_restores_original_state() -> None:
"""Context manager enables then restores original rule state."""
rule = next(r for r in DEFAULT_RULES if r.label == "core-git-scp")
orig_state = (rule.is_explicit, rule.weight)

# Use context manager
with ssh_style_url_detection():
assert rule.is_explicit is True
assert rule.weight == 100

# After context, state should be back to original
assert (rule.is_explicit, rule.weight) == orig_state