Skip to content

Commit a996efc

Browse files
noirbizarreGabDug
andauthored
feat(sync): automatically update aliased repositories URLs (#38)
* feat(sync): automatically update aliased repositories URLs --------- Co-authored-by: Gabriel Dugny <[email protected]> Co-authored-by: Gabriel Dugny <[email protected]>
1 parent a2bda3a commit a996efc

File tree

9 files changed

+136
-9
lines changed

9 files changed

+136
-9
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ Feel free to open an issue or a PR if you have any idea, or if you want to help!
170170
- [ ] Check ref existence before writing?
171171
- [ ] New feature to convert from pre-commit online to local?
172172
- [ ] Warning if pre-commit CI auto update is also set?
173-
- [ ] Support automatic repository URL update (from legacy aliased repositories)
173+
- [x] Support automatic repository URL update (from legacy aliased repositories)
174174

175175

176176
## Inspiration

src/sync_pre_commit_lock/actions/sync_hooks.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ def get_pre_commit_repo_new_version(
138138
)
139139
return None
140140

141+
def get_pre_commit_repo_new_url(self, url: str) -> str:
142+
return self.mapping[self.mapping_reverse_by_url[url]]["repo"]
143+
141144
def get_pre_commit_repo_new_hooks(self, hooks: Sequence[PreCommitHook]) -> Sequence[PreCommitHook]:
142145
return [self.get_pre_commit_repo_new_hook(hook) for hook in hooks]
143146

@@ -173,7 +176,7 @@ def analyze_repos(
173176
continue
174177

175178
new_repo = PreCommitRepo(
176-
repo=pre_commit_repo.repo,
179+
repo=self.get_pre_commit_repo_new_url(pre_commit_repo.repo),
177180
rev=self.get_pre_commit_repo_new_version(pre_commit_repo) or pre_commit_repo.rev,
178181
hooks=self.get_pre_commit_repo_new_hooks(pre_commit_repo.hooks),
179182
)

src/sync_pre_commit_lock/pdm_plugin.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from sync_pre_commit_lock.actions.install_hooks import SetupPreCommitHooks
1818
from sync_pre_commit_lock.actions.sync_hooks import GenericLockedPackage, SyncPreCommitHooksVersion
1919
from sync_pre_commit_lock.config import SyncPreCommitLockConfig, load_config
20+
from sync_pre_commit_lock.utils import url_diff
2021

2122
if TYPE_CHECKING:
2223
import argparse
@@ -59,8 +60,9 @@ def error(self, msg: str) -> None:
5960
def success(self, msg: str) -> None:
6061
self.ui.echo("[success]" + self.prefix_lines(msg) + "[/success]", verbosity=Verbosity.NORMAL)
6162

62-
def _format_repo_url(self, repo_url: str, package_name: str) -> str:
63-
return repo_url.replace(package_name, f"[cyan][bold]{package_name}[/bold][/cyan]")
63+
def _format_repo_url(self, old_repo_url: str, new_repo_url: str, package_name: str) -> str:
64+
url = url_diff(old_repo_url, new_repo_url, "[cyan]{[/][red]", "[/red][cyan] -> [/][green]", "[/][cyan]}[/]")
65+
return url.replace(package_name, f"[cyan][bold]{package_name}[/bold][/cyan]")
6466

6567
def list_updated_packages(self, packages: dict[str, tuple[PreCommitRepo, PreCommitRepo]]) -> None:
6668
"""
@@ -75,7 +77,7 @@ def _format_repo(self, package: str, old: PreCommitRepo, new: PreCommitRepo) ->
7577
new_version = new.rev != old.rev
7678
repo = (
7779
f"[info]{self.plugin_prefix}[/info] {self.success_list_token}",
78-
f"[info]{self._format_repo_url(old.repo, package)}[/info]",
80+
f"[info]{self._format_repo_url(old.repo, new.repo, package)}[/info]",
7981
" ",
8082
f"[error]{old.rev}[/error]" if new_version else "",
8183
"[info]->[/info]" if new_version else "",

src/sync_pre_commit_lock/poetry_plugin.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from sync_pre_commit_lock.actions.install_hooks import SetupPreCommitHooks
2424
from sync_pre_commit_lock.actions.sync_hooks import GenericLockedPackage, SyncPreCommitHooksVersion
2525
from sync_pre_commit_lock.config import load_config
26+
from sync_pre_commit_lock.utils import url_diff
2627

2728
if TYPE_CHECKING:
2829
from collections.abc import Sequence
@@ -71,7 +72,7 @@ def _format_repo(self, package: str, old: PreCommitRepo, new: PreCommitRepo) ->
7172
new_version = new.rev != old.rev
7273
repo = (
7374
f"<info>{self.plugin_prefix} {self.success_list_token}",
74-
self._format_repo_url(old.repo, package),
75+
self._format_repo_url(old.repo, new.repo, package),
7576
" ",
7677
f"<warning>{old.rev}</>" if new_version else "",
7778
"->" if new_version else "",
@@ -85,8 +86,9 @@ def _format_repo(self, package: str, old: PreCommitRepo, new: PreCommitRepo) ->
8586
]
8687
return [repo, *hooks] if hooks else [repo]
8788

88-
def _format_repo_url(self, repo_url: str, package_name: str) -> str:
89-
return repo_url.replace(package_name, f"<c1>{package_name}</>")
89+
def _format_repo_url(self, old_repo_url: str, new_repo_url: str, package_name: str) -> str:
90+
url = url_diff(old_repo_url, new_repo_url, "<c1>{</><warning>", "</><c1> -> </><success>", "</><c1>}</>")
91+
return url.replace(package_name, f"<c1>{package_name}</>")
9092

9193
def _format_hook(self, old: PreCommitHook, new: PreCommitHook, last: bool) -> Sequence[Sequence[str]]:
9294
if not (nb_deps := len(old.additional_dependencies)):

src/sync_pre_commit_lock/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from os.path import commonprefix
12
from urllib.parse import urlparse, urlunparse
23

34

@@ -38,3 +39,14 @@ def normalize_git_url(url: str) -> str:
3839
normalized_url = normalized_url[:-1]
3940

4041
return normalized_url
42+
43+
44+
def url_diff(old: str, new: str, diff_open: str = "{", diff_separator: str = " -> ", diff_close: str = "}") -> str:
45+
"""Represent a change of URL highlighting only the changed part"""
46+
if old == new:
47+
return new
48+
prefix = commonprefix((old, new))
49+
old, new = old.removeprefix(prefix), new.removeprefix(prefix)
50+
suffix = commonprefix((old[::-1], new[::-1]))[::-1]
51+
old, new = old.removesuffix(suffix), new.removesuffix(suffix)
52+
return f"{prefix}{diff_open}{old}{diff_separator}{new}{diff_close}{suffix}"

tests/test_actions/test_sync_hooks.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,51 @@ def test_analyze_repos_local_but_additional_dependencies() -> None:
462462
assert to_fix == {
463463
pre_commit_repo: PreCommitRepo("https://repo_url", "1.2.3", [PreCommitHook("hook", ["lib-name==2.0.0"])])
464464
}
465+
466+
467+
MOCK_DEP_MAPPING = {"dep": {"repo": "https://some.place", "rev": "${dev}"}}
468+
MOCK_REPO_ALIASES = {"https://some.place": ("https://some.old.place",)}
469+
470+
471+
@patch("sync_pre_commit_lock.actions.sync_hooks.DEPENDENCY_MAPPING", MOCK_DEP_MAPPING)
472+
@patch("sync_pre_commit_lock.actions.sync_hooks.REPOSITORY_ALIASES", MOCK_REPO_ALIASES)
473+
def test_analyze_repos_renamed() -> None:
474+
printer = MagicMock(spec=Printer)
475+
pre_commit_config_file_path = MagicMock(spec=Path)
476+
locked_packages: dict[str, GenericLockedPackage] = {"ruff": GenericLockedPackage("dep", "1.2.3")}
477+
plugin_config = SyncPreCommitLockConfig()
478+
479+
syncer = SyncPreCommitHooksVersion(
480+
printer=printer,
481+
pre_commit_config_file_path=pre_commit_config_file_path,
482+
locked_packages=locked_packages,
483+
plugin_config=plugin_config,
484+
)
485+
pre_commit_repo = PreCommitRepo("https://some.old.place", "1.2.3")
486+
pre_commit_repos = {pre_commit_repo}
487+
488+
to_fix, _ = syncer.analyze_repos(pre_commit_repos)
489+
490+
assert to_fix == {pre_commit_repo: PreCommitRepo("https://some.place", "1.2.3")}
491+
492+
493+
@patch("sync_pre_commit_lock.actions.sync_hooks.DEPENDENCY_MAPPING", MOCK_DEP_MAPPING)
494+
@patch("sync_pre_commit_lock.actions.sync_hooks.REPOSITORY_ALIASES", MOCK_REPO_ALIASES)
495+
def test_analyze_repos_already_last_url() -> None:
496+
printer = MagicMock(spec=Printer)
497+
pre_commit_config_file_path = MagicMock(spec=Path)
498+
locked_packages: dict[str, GenericLockedPackage] = {"ruff": GenericLockedPackage("dep", "1.2.3")}
499+
plugin_config = SyncPreCommitLockConfig()
500+
501+
syncer = SyncPreCommitHooksVersion(
502+
printer=printer,
503+
pre_commit_config_file_path=pre_commit_config_file_path,
504+
locked_packages=locked_packages,
505+
plugin_config=plugin_config,
506+
)
507+
pre_commit_repo = PreCommitRepo("https://some.place", "1.2.3")
508+
pre_commit_repos = {pre_commit_repo}
509+
510+
to_fix, _ = syncer.analyze_repos(pre_commit_repos)
511+
512+
assert to_fix == {}

tests/test_pdm/test_pdm_plugin.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,19 @@ def test_pdm_printer_list_success_repo_with_multiple_hooks_and_additional_depend
151151
assert "[sync-pre-commit-lock] └ 2nd-hook" in captured.out
152152
assert "[sync-pre-commit-lock] ├ dep * -> 0.1.2" in captured.out
153153
assert "[sync-pre-commit-lock] └ other >=0.42 -> 3.4.5" in captured.out
154+
155+
156+
def test_pdm_printer_list_success_renamed_repository(capsys: pytest.CaptureFixture[str]) -> None:
157+
printer = PDMPrinter(UI())
158+
159+
printer.list_updated_packages(
160+
{
161+
"package": (
162+
PreCommitRepo("https://old.repo.local/test", "rev1", [PreCommitHook("hook")]),
163+
PreCommitRepo("https://new.repo.local/test", "rev2", [PreCommitHook("hook")]),
164+
),
165+
}
166+
)
167+
captured = capsys.readouterr()
168+
169+
assert "[sync-pre-commit-lock] ✔ https://{old -> new}.repo.local/test rev1 -> rev2" in captured.out

tests/test_poetry/test_poetry_plugin.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,36 @@ def _write(message: str, new_line: bool = False):
220220
assert "[sync-pre-commit-lock] └ other >=0.42 -> 3.4.5" in out
221221

222222

223+
def test_poetry_printer_list_success_renamed_repository(capsys: pytest.CaptureFixture[str]) -> None:
224+
from cleo.io.inputs.input import Input
225+
from cleo.io.io import IO
226+
from cleo.io.outputs.output import Output
227+
228+
from sync_pre_commit_lock.poetry_plugin import PoetryPrinter
229+
230+
output = Output()
231+
232+
def _write(message: str, new_line: bool = False):
233+
print(message) # noqa: T201
234+
235+
output._write = _write
236+
printer = PoetryPrinter(IO(input=Input(), output=output, error_output=output))
237+
238+
printer.list_updated_packages(
239+
{
240+
"package": (
241+
PreCommitRepo("https://old.repo.local/test", "rev1", [PreCommitHook("hook")]),
242+
PreCommitRepo("https://new.repo.local/test", "rev2", [PreCommitHook("hook")]),
243+
),
244+
}
245+
)
246+
captured = capsys.readouterr()
247+
# Remove all <..> tags, as we don't have the real parser
248+
out = re.sub(r"<[^>]*>", "", captured.out)
249+
250+
assert "[sync-pre-commit-lock] • https://{old -> new}.repo.local/test rev1 -> rev2" in out
251+
252+
223253
def test_direct_command_invocation():
224254
with pytest.raises(RuntimeError, match="self.application is None"):
225255
SyncPreCommitPoetryCommand().handle()

tests/test_utils.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from sync_pre_commit_lock.utils import normalize_git_url
3+
from sync_pre_commit_lock.utils import normalize_git_url, url_diff
44

55

66
# Here are the test cases
@@ -23,3 +23,17 @@
2323
)
2424
def test_normalize_git_url(url: str, expected: str) -> None:
2525
assert normalize_git_url(url) == expected
26+
27+
28+
@pytest.mark.parametrize(
29+
"old,new,expected",
30+
[
31+
("https://some.place", "https://some.place", "https://some.place"),
32+
("https://some.old.place", "https://some.new.place", "https://some.{old -> new}.place"),
33+
("https://some.place", "https://another.place", "https://{some -> another}.place"),
34+
("https://some.place/old", "https://a.different/place", "https://{some.place/old -> a.different/place}"),
35+
("https://some.place/old", "https://some.place/new", "https://some.place/{old -> new}"),
36+
],
37+
)
38+
def test_url_diff(old: str, new: str, expected: str):
39+
assert url_diff(old, new) == expected

0 commit comments

Comments
 (0)