refactor(mirror): enforce primary origin URL and align mirror resolution logic
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-env-virtual (push) Has been cancelled
Mark stable commit / test-env-nix (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / linter-shell (push) Has been cancelled
Mark stable commit / linter-python (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-env-virtual (push) Has been cancelled
Mark stable commit / test-env-nix (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / linter-shell (push) Has been cancelled
Mark stable commit / linter-python (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
- Resolve primary remote via RepoMirrorContext (origin → file order → config → default) - Always set origin fetch and push URL to primary - Add additional mirrors as extra push URLs without duplication - Update remote provisioning and setup commands to use context-based resolution - Adjust and extend unit tests to cover new origin/push behavior https://chatgpt.com/share/693f4538-42d4-800f-98c2-2ec264fd2e19
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
@@ -9,117 +6,61 @@ from unittest.mock import patch
|
||||
from pkgmgr.actions.mirror.git_remote import (
|
||||
build_default_ssh_url,
|
||||
determine_primary_remote_url,
|
||||
current_origin_url,
|
||||
has_origin_remote,
|
||||
)
|
||||
from pkgmgr.actions.mirror.types import MirrorMap, Repository
|
||||
from pkgmgr.actions.mirror.types import RepoMirrorContext
|
||||
|
||||
|
||||
class TestMirrorGitRemote(unittest.TestCase):
|
||||
"""
|
||||
Unit tests for SSH URL and primary remote selection logic.
|
||||
"""
|
||||
def _ctx(self, *, file=None, config=None) -> RepoMirrorContext:
|
||||
return RepoMirrorContext(
|
||||
identifier="repo",
|
||||
repo_dir="/tmp/repo",
|
||||
config_mirrors=config or {},
|
||||
file_mirrors=file or {},
|
||||
)
|
||||
|
||||
def test_build_default_ssh_url_without_port(self) -> None:
|
||||
repo: Repository = {
|
||||
def test_build_default_ssh_url(self) -> None:
|
||||
repo = {
|
||||
"provider": "github.com",
|
||||
"account": "kevinveenbirkenbach",
|
||||
"repository": "package-manager",
|
||||
"account": "alice",
|
||||
"repository": "repo",
|
||||
}
|
||||
self.assertEqual(
|
||||
build_default_ssh_url(repo),
|
||||
"git@github.com:alice/repo.git",
|
||||
)
|
||||
|
||||
url = build_default_ssh_url(repo)
|
||||
self.assertEqual(url, "git@github.com:kevinveenbirkenbach/package-manager.git")
|
||||
def test_determine_primary_prefers_origin(self) -> None:
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
ctx = self._ctx(config={"origin": "git@github.com:alice/repo.git"})
|
||||
self.assertEqual(
|
||||
determine_primary_remote_url(repo, ctx),
|
||||
"git@github.com:alice/repo.git",
|
||||
)
|
||||
|
||||
def test_build_default_ssh_url_with_port(self) -> None:
|
||||
repo: Repository = {
|
||||
"provider": "code.cymais.cloud",
|
||||
"account": "kevinveenbirkenbach",
|
||||
"repository": "pkgmgr",
|
||||
"port": 2201,
|
||||
}
|
||||
def test_determine_primary_uses_file_order(self) -> None:
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
ctx = self._ctx(
|
||||
file={
|
||||
"first": "git@a/first.git",
|
||||
"second": "git@a/second.git",
|
||||
}
|
||||
)
|
||||
self.assertEqual(
|
||||
determine_primary_remote_url(repo, ctx),
|
||||
"git@a/first.git",
|
||||
)
|
||||
|
||||
url = build_default_ssh_url(repo)
|
||||
self.assertEqual(url, "ssh://git@code.cymais.cloud:2201/kevinveenbirkenbach/pkgmgr.git")
|
||||
def test_determine_primary_fallback_default(self) -> None:
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
ctx = self._ctx()
|
||||
self.assertEqual(
|
||||
determine_primary_remote_url(repo, ctx),
|
||||
"git@github.com:alice/repo.git",
|
||||
)
|
||||
|
||||
def test_build_default_ssh_url_missing_fields_returns_none(self) -> None:
|
||||
repo: Repository = {
|
||||
"provider": "github.com",
|
||||
"account": "kevinveenbirkenbach",
|
||||
}
|
||||
|
||||
url = build_default_ssh_url(repo)
|
||||
self.assertIsNone(url)
|
||||
|
||||
def test_determine_primary_remote_url_prefers_origin_in_resolved_mirrors(self) -> None:
|
||||
repo: Repository = {
|
||||
"provider": "github.com",
|
||||
"account": "kevinveenbirkenbach",
|
||||
"repository": "package-manager",
|
||||
}
|
||||
mirrors: MirrorMap = {
|
||||
"origin": "git@github.com:kevinveenbirkenbach/package-manager.git",
|
||||
"backup": "ssh://git@git.veen.world:2201/kevinveenbirkenbach/pkgmgr.git",
|
||||
}
|
||||
|
||||
url = determine_primary_remote_url(repo, mirrors)
|
||||
self.assertEqual(url, "git@github.com:kevinveenbirkenbach/package-manager.git")
|
||||
|
||||
def test_determine_primary_remote_url_uses_any_mirror_if_no_origin(self) -> None:
|
||||
repo: Repository = {
|
||||
"provider": "github.com",
|
||||
"account": "kevinveenbirkenbach",
|
||||
"repository": "package-manager",
|
||||
}
|
||||
mirrors: MirrorMap = {
|
||||
"backup": "ssh://git@git.veen.world:2201/kevinveenbirkenbach/pkgmgr.git",
|
||||
"mirror2": "ssh://git@code.cymais.cloud:2201/kevinveenbirkenbach/pkgmgr.git",
|
||||
}
|
||||
|
||||
url = determine_primary_remote_url(repo, mirrors)
|
||||
self.assertEqual(url, "ssh://git@git.veen.world:2201/kevinveenbirkenbach/pkgmgr.git")
|
||||
|
||||
def test_determine_primary_remote_url_falls_back_to_default_ssh(self) -> None:
|
||||
repo: Repository = {
|
||||
"provider": "github.com",
|
||||
"account": "kevinveenbirkenbach",
|
||||
"repository": "package-manager",
|
||||
}
|
||||
mirrors: MirrorMap = {}
|
||||
|
||||
url = determine_primary_remote_url(repo, mirrors)
|
||||
self.assertEqual(url, "git@github.com:kevinveenbirkenbach/package-manager.git")
|
||||
|
||||
@patch("pkgmgr.actions.mirror.git_remote.run_git")
|
||||
def test_current_origin_url_returns_value(self, mock_run_git) -> None:
|
||||
mock_run_git.return_value = "git@github.com:alice/repo.git\n"
|
||||
self.assertEqual(current_origin_url("/tmp/repo"), "git@github.com:alice/repo.git")
|
||||
mock_run_git.assert_called_once_with(["remote", "get-url", "origin"], cwd="/tmp/repo")
|
||||
|
||||
@patch("pkgmgr.actions.mirror.git_remote.run_git")
|
||||
def test_current_origin_url_returns_none_on_git_error(self, mock_run_git) -> None:
|
||||
from pkgmgr.core.git import GitError
|
||||
|
||||
mock_run_git.side_effect = GitError("fail")
|
||||
self.assertIsNone(current_origin_url("/tmp/repo"))
|
||||
|
||||
@patch("pkgmgr.actions.mirror.git_remote.run_git")
|
||||
def test_has_origin_remote_true(self, mock_run_git) -> None:
|
||||
mock_run_git.return_value = "origin\nupstream\n"
|
||||
@patch("pkgmgr.actions.mirror.git_remote._safe_git_output")
|
||||
def test_has_origin_remote(self, m_out) -> None:
|
||||
m_out.return_value = "origin\nupstream\n"
|
||||
self.assertTrue(has_origin_remote("/tmp/repo"))
|
||||
mock_run_git.assert_called_once_with(["remote"], cwd="/tmp/repo")
|
||||
|
||||
@patch("pkgmgr.actions.mirror.git_remote.run_git")
|
||||
def test_has_origin_remote_false_on_missing_remote(self, mock_run_git) -> None:
|
||||
mock_run_git.return_value = "upstream\n"
|
||||
self.assertFalse(has_origin_remote("/tmp/repo"))
|
||||
|
||||
@patch("pkgmgr.actions.mirror.git_remote.run_git")
|
||||
def test_has_origin_remote_false_on_git_error(self, mock_run_git) -> None:
|
||||
from pkgmgr.core.git import GitError
|
||||
|
||||
mock_run_git.side_effect = GitError("fail")
|
||||
self.assertFalse(has_origin_remote("/tmp/repo"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.actions.mirror.git_remote import ensure_origin_remote
|
||||
from pkgmgr.actions.mirror.types import RepoMirrorContext
|
||||
|
||||
|
||||
class TestGitRemotePrimaryPush(unittest.TestCase):
|
||||
def test_origin_created_and_extra_push_added(self) -> None:
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
ctx = RepoMirrorContext(
|
||||
identifier="repo",
|
||||
repo_dir="/tmp/repo",
|
||||
config_mirrors={},
|
||||
file_mirrors={
|
||||
"primary": "git@github.com:alice/repo.git",
|
||||
"backup": "git@github.com:alice/repo-backup.git",
|
||||
},
|
||||
)
|
||||
|
||||
executed: list[str] = []
|
||||
|
||||
def fake_run(cmd: str, cwd: str, preview: bool) -> None:
|
||||
executed.append(cmd)
|
||||
|
||||
def fake_git(args, cwd):
|
||||
if args == ["remote"]:
|
||||
return ""
|
||||
if args == ["remote", "get-url", "--push", "--all", "origin"]:
|
||||
return "git@github.com:alice/repo.git\n"
|
||||
return ""
|
||||
|
||||
with patch("os.path.isdir", return_value=True), patch(
|
||||
"pkgmgr.actions.mirror.git_remote.run_command", side_effect=fake_run
|
||||
), patch(
|
||||
"pkgmgr.actions.mirror.git_remote._safe_git_output", side_effect=fake_git
|
||||
):
|
||||
ensure_origin_remote(repo, ctx, preview=False)
|
||||
|
||||
self.assertEqual(
|
||||
executed,
|
||||
[
|
||||
"git remote add origin git@github.com:alice/repo.git",
|
||||
"git remote set-url origin git@github.com:alice/repo.git",
|
||||
"git remote set-url --push origin git@github.com:alice/repo.git",
|
||||
"git remote set-url --add --push origin git@github.com:alice/repo-backup.git",
|
||||
],
|
||||
)
|
||||
@@ -1,123 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, PropertyMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.actions.mirror.setup_cmd import setup_mirrors
|
||||
from pkgmgr.actions.mirror.types import RepoMirrorContext
|
||||
|
||||
|
||||
class TestMirrorSetupCmd(unittest.TestCase):
|
||||
"""
|
||||
Unit tests for mirror setup orchestration (local + remote).
|
||||
"""
|
||||
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.ensure_origin_remote")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
def test_setup_mirrors_local_calls_ensure_origin_remote(
|
||||
def _ctx(
|
||||
self,
|
||||
mock_build_context,
|
||||
mock_ensure_origin,
|
||||
) -> None:
|
||||
ctx = MagicMock()
|
||||
ctx.identifier = "repo-id"
|
||||
ctx.repo_dir = "/tmp/repo"
|
||||
ctx.config_mirrors = {}
|
||||
ctx.file_mirrors = {}
|
||||
type(ctx).resolved_mirrors = PropertyMock(return_value={})
|
||||
mock_build_context.return_value = ctx
|
||||
*,
|
||||
repo_dir: str = "/tmp/repo",
|
||||
resolved: dict[str, str] | None = None,
|
||||
) -> RepoMirrorContext:
|
||||
# RepoMirrorContext derives resolved via property (config + file)
|
||||
# We feed mirrors via file_mirrors to keep insertion order realistic.
|
||||
return RepoMirrorContext(
|
||||
identifier="repo-id",
|
||||
repo_dir=repo_dir,
|
||||
config_mirrors={},
|
||||
file_mirrors=resolved or {},
|
||||
)
|
||||
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.ensure_origin_remote")
|
||||
def test_setup_mirrors_local_calls_ensure_origin_remote(self, m_ensure, m_ctx) -> None:
|
||||
m_ctx.return_value = self._ctx(repo_dir="/tmp/repo", resolved={"primary": "git@x/y.git"})
|
||||
|
||||
repos = [{"provider": "github.com", "account": "alice", "repository": "repo"}]
|
||||
setup_mirrors(
|
||||
selected_repos=[repo],
|
||||
repositories_base_dir="/base",
|
||||
all_repos=[repo],
|
||||
selected_repos=repos,
|
||||
repositories_base_dir="/tmp",
|
||||
all_repos=repos,
|
||||
preview=True,
|
||||
local=True,
|
||||
remote=False,
|
||||
ensure_remote=False,
|
||||
)
|
||||
|
||||
mock_ensure_origin.assert_called_once()
|
||||
args, kwargs = mock_ensure_origin.call_args
|
||||
self.assertEqual(args[0], repo)
|
||||
self.assertEqual(kwargs.get("preview"), True)
|
||||
self.assertEqual(m_ensure.call_count, 1)
|
||||
args, kwargs = m_ensure.call_args
|
||||
|
||||
# ensure_origin_remote(repo, ctx, preview) may be positional or kw.
|
||||
# Accept both to avoid coupling tests to call style.
|
||||
if "preview" in kwargs:
|
||||
self.assertTrue(kwargs["preview"])
|
||||
else:
|
||||
# args: (repo, ctx, preview)
|
||||
self.assertTrue(args[2])
|
||||
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.ensure_remote_repository")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.probe_mirror")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
def test_setup_mirrors_remote_provisions_when_enabled(
|
||||
self,
|
||||
mock_build_context,
|
||||
mock_probe,
|
||||
mock_ensure_remote_repository,
|
||||
) -> None:
|
||||
ctx = MagicMock()
|
||||
ctx.identifier = "repo-id"
|
||||
ctx.repo_dir = "/tmp/repo"
|
||||
ctx.config_mirrors = {"origin": "git@github.com:alice/repo.git"}
|
||||
ctx.file_mirrors = {}
|
||||
type(ctx).resolved_mirrors = PropertyMock(return_value={"origin": "git@github.com:alice/repo.git"})
|
||||
mock_build_context.return_value = ctx
|
||||
|
||||
mock_probe.return_value = (True, "")
|
||||
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
|
||||
setup_mirrors(
|
||||
selected_repos=[repo],
|
||||
repositories_base_dir="/base",
|
||||
all_repos=[repo],
|
||||
preview=False,
|
||||
local=False,
|
||||
remote=True,
|
||||
ensure_remote=True,
|
||||
)
|
||||
|
||||
mock_ensure_remote_repository.assert_called_once()
|
||||
mock_probe.assert_called_once()
|
||||
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.ensure_remote_repository")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.probe_mirror")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
def test_setup_mirrors_remote_probes_all_resolved_mirrors(
|
||||
self,
|
||||
mock_build_context,
|
||||
mock_probe,
|
||||
mock_ensure_remote_repository,
|
||||
) -> None:
|
||||
ctx = MagicMock()
|
||||
ctx.identifier = "repo-id"
|
||||
ctx.repo_dir = "/tmp/repo"
|
||||
ctx.config_mirrors = {}
|
||||
ctx.file_mirrors = {}
|
||||
type(ctx).resolved_mirrors = PropertyMock(
|
||||
return_value={
|
||||
"mirror": "git@github.com:alice/repo.git",
|
||||
"backup": "ssh://git@git.veen.world:2201/alice/repo.git",
|
||||
}
|
||||
)
|
||||
mock_build_context.return_value = ctx
|
||||
|
||||
mock_probe.return_value = (True, "")
|
||||
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.determine_primary_remote_url")
|
||||
def test_setup_mirrors_remote_no_mirrors_probes_primary(self, m_primary, m_probe, m_ctx) -> None:
|
||||
m_ctx.return_value = self._ctx(repo_dir="/tmp/repo", resolved={})
|
||||
m_primary.return_value = "git@github.com:alice/repo.git"
|
||||
m_probe.return_value = (True, "")
|
||||
|
||||
repos = [{"provider": "github.com", "account": "alice", "repository": "repo"}]
|
||||
setup_mirrors(
|
||||
selected_repos=[repo],
|
||||
repositories_base_dir="/base",
|
||||
all_repos=[repo],
|
||||
preview=False,
|
||||
selected_repos=repos,
|
||||
repositories_base_dir="/tmp",
|
||||
all_repos=repos,
|
||||
preview=True,
|
||||
local=False,
|
||||
remote=True,
|
||||
ensure_remote=False,
|
||||
)
|
||||
|
||||
mock_ensure_remote_repository.assert_not_called()
|
||||
self.assertEqual(mock_probe.call_count, 2)
|
||||
m_primary.assert_called()
|
||||
m_probe.assert_called_with("git@github.com:alice/repo.git", "/tmp/repo")
|
||||
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.probe_mirror")
|
||||
def test_setup_mirrors_remote_with_mirrors_probes_each(self, m_probe, m_ctx) -> None:
|
||||
m_ctx.return_value = self._ctx(
|
||||
repo_dir="/tmp/repo",
|
||||
resolved={
|
||||
"origin": "git@github.com:alice/repo.git",
|
||||
"backup": "ssh://git@git.veen.world:2201/alice/repo.git",
|
||||
},
|
||||
)
|
||||
m_probe.return_value = (True, "")
|
||||
|
||||
repos = [{"provider": "github.com", "account": "alice", "repository": "repo"}]
|
||||
setup_mirrors(
|
||||
selected_repos=repos,
|
||||
repositories_base_dir="/tmp",
|
||||
all_repos=repos,
|
||||
preview=True,
|
||||
local=False,
|
||||
remote=True,
|
||||
ensure_remote=False,
|
||||
)
|
||||
|
||||
self.assertEqual(m_probe.call_count, 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user