Merge branch 'feature/21' into develop

This commit is contained in:
Tiara Rodney 2026-06-06 16:15:41 +02:00
commit afdb9b2906
Signed by: tiara
GPG key ID: 5CD8EC1D46106723
3 changed files with 78 additions and 32 deletions

2
TODO
View file

@ -284,7 +284,7 @@ Content-Type: application/issue
ID: 21 ID: 21
Type: feature Type: feature
Title: relax host restriction in vcs.git parse_base_url and parse_repo_name Title: relax host restriction in vcs.git parse_base_url and parse_repo_name
Status: in-progress Status: done
Priority: high Priority: high
Created: 2026-06-06 Created: 2026-06-06
Relationships: Relationships:

View file

@ -26,54 +26,40 @@ class GitError(Exception):
def parse_base_url(base_url: str) -> str: def parse_base_url(base_url: str) -> str:
"""Extract workspace from an SCP-style Bitbucket base URL. """Extract the workspace from an SCP-style base URL.
The host part must be exactly ``bitbucket.org`` bootstrapping Accepts any host (Bitbucket, Forgejo, GitHub, ...) as long as
requires the Bitbucket API, so other hosts are rejected. the URL is SCP-style::
>>> _parse_base_url("git@bitbucket.org:byteb4rb1e") git@bitbucket.org:byteb4rb1e/foo.git byteb4rb1e
'byteb4rb1e' git@git.code.tiararodney.com:h5p-mirror/foo.git h5p-mirror
""" """
# SCP-style: git@bitbucket.org:workspace # SCP-style: git@host:workspace/repo
if ":" not in base_url or "//" in base_url: if ":" not in base_url or "//" in base_url:
raise ValueError( raise ValueError(
f"Expected SCP-style URL (git@bitbucket.org:workspace), " f"Expected SCP-style URL (git@host:workspace), "
f"got: {base_url}" f"got: {base_url}"
) )
host_part, workspace = base_url.split(":", 1) _, workspace = base_url.split(":", 1)
# host_part is e.g. "git@bitbucket.org" return str(Path(workspace).parent)
host = host_part.split("@", 1)[-1]
if host != "bitbucket.org":
raise ValueError(
f"Mirror base URL must target bitbucket.org, "
f"got host: {host}"
)
return Path(workspace).parent
def parse_repo_name(base_url: str) -> str: def parse_repo_name(base_url: str) -> str:
"""Extract workspace from an SCP-style Bitbucket base URL. """Extract the repository name from an SCP-style base URL.
The host part must be exactly ``bitbucket.org`` bootstrapping Accepts any host (Bitbucket, Forgejo, GitHub, ...) as long as
requires the Bitbucket API, so other hosts are rejected. the URL is SCP-style::
>>> _parse_base_url("git@bitbucket.org:byteb4rb1e") git@bitbucket.org:byteb4rb1e/foo.git foo
'byteb4rb1e' git@git.code.tiararodney.com:h5p-mirror/foo.git foo
""" """
# SCP-style: git@bitbucket.org:workspace # SCP-style: git@host:workspace/repo
if ":" not in base_url or "//" in base_url: if ":" not in base_url or "//" in base_url:
raise ValueError( raise ValueError(
f"Expected SCP-style URL (git@bitbucket.org:workspace), " f"Expected SCP-style URL (git@host:workspace), "
f"got: {base_url}" f"got: {base_url}"
) )
host_part, workspace = base_url.split(":", 1) _, workspace = base_url.split(":", 1)
# host_part is e.g. "git@bitbucket.org"
host = host_part.split("@", 1)[-1]
if host != "bitbucket.org":
raise ValueError(
f"Mirror base URL must target bitbucket.org, "
f"got host: {host}"
)
return Path(workspace).name.split('.')[0] return Path(workspace).name.split('.')[0]

View file

@ -0,0 +1,60 @@
"""Tests for the git subprocess wrapper's URL parsing helpers."""
import pytest
from byteb4rb1e.utils.vcs.git import parse_base_url, parse_repo_name
class TestParseBaseUrl:
def test_bitbucket(self) -> None:
result = parse_base_url("git@bitbucket.org:byteb4rb1e/foo.git")
assert result == "byteb4rb1e"
def test_forgejo_host(self) -> None:
result = parse_base_url(
"git@git.code.tiararodney.com:h5p-mirror/foo.git"
)
assert result == "h5p-mirror"
def test_github_host(self) -> None:
result = parse_base_url("git@github.com:h5p/h5p-multi-choice.git")
assert result == "h5p"
def test_returns_str(self) -> None:
result = parse_base_url("git@bitbucket.org:byteb4rb1e/foo.git")
assert isinstance(result, str)
def test_rejects_https_url(self) -> None:
with pytest.raises(ValueError):
parse_base_url("https://bitbucket.org/byteb4rb1e/foo.git")
def test_rejects_url_without_colon(self) -> None:
with pytest.raises(ValueError):
parse_base_url("bitbucket.org/byteb4rb1e/foo.git")
class TestParseRepoName:
def test_bitbucket(self) -> None:
assert parse_repo_name(
"git@bitbucket.org:byteb4rb1e/foo.git"
) == "foo"
def test_forgejo_host(self) -> None:
assert parse_repo_name(
"git@git.code.tiararodney.com:h5p-mirror/foo.git"
) == "foo"
def test_without_git_suffix(self) -> None:
assert parse_repo_name(
"git@git.code.tiararodney.com:h5p-mirror/foo"
) == "foo"
def test_rejects_https_url(self) -> None:
with pytest.raises(ValueError):
parse_repo_name("https://git.code.tiararodney.com/x/foo.git")
def test_rejects_url_without_colon(self) -> None:
with pytest.raises(ValueError):
parse_repo_name("git.code.tiararodney.com/x/foo.git")