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
Type: feature
Title: relax host restriction in vcs.git parse_base_url and parse_repo_name
Status: in-progress
Status: done
Priority: high
Created: 2026-06-06
Relationships:

View file

@ -26,54 +26,40 @@ class GitError(Exception):
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
requires the Bitbucket API, so other hosts are rejected.
Accepts any host (Bitbucket, Forgejo, GitHub, ...) as long as
the URL is SCP-style::
>>> _parse_base_url("git@bitbucket.org:byteb4rb1e")
'byteb4rb1e'
git@bitbucket.org:byteb4rb1e/foo.git 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:
raise ValueError(
f"Expected SCP-style URL (git@bitbucket.org:workspace), "
f"Expected SCP-style URL (git@host:workspace), "
f"got: {base_url}"
)
host_part, 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).parent
_, workspace = base_url.split(":", 1)
return str(Path(workspace).parent)
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
requires the Bitbucket API, so other hosts are rejected.
Accepts any host (Bitbucket, Forgejo, GitHub, ...) as long as
the URL is SCP-style::
>>> _parse_base_url("git@bitbucket.org:byteb4rb1e")
'byteb4rb1e'
git@bitbucket.org:byteb4rb1e/foo.git foo
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:
raise ValueError(
f"Expected SCP-style URL (git@bitbucket.org:workspace), "
f"Expected SCP-style URL (git@host:workspace), "
f"got: {base_url}"
)
host_part, 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}"
)
_, workspace = base_url.split(":", 1)
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")