Skip to content

Commit

Permalink
feat: improve rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
GabDug committed Sep 9, 2023
1 parent 054123c commit 74848d5
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 22 deletions.
29 changes: 19 additions & 10 deletions src/sync_pre_commit_lock/actions/sync_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,27 @@ def execute(self) -> None:
return

mapping, mapping_reverse_by_url = self.build_mapping()
# XXX We should have the list of packages mapped, but already up to date and print it
to_fix, in_sync = self.analyze_repos(pre_commit_config_data.repos_normalized, mapping, mapping_reverse_by_url)

to_fix = self.analyze_repos(pre_commit_config_data.repos_normalized, mapping, mapping_reverse_by_url)

if len(to_fix) == 0 and len(in_sync) == 0:
self.printer.info("No pre-commit hook detected that matches a locked package.")
return
if len(to_fix) == 0:
self.printer.info("All matched pre-commit hooks already in sync with the lockfile!")
packages_str = ", ".join(f"{mapping_reverse_by_url[repo.repo]} ({rev})" for repo, rev in in_sync.items())
self.printer.info(f"All pre-commit hooks are already up to date with the lockfile: {packages_str}")
return

self.printer.info("Detected pre-commit hooks that can be updated to match the lockfile:")
for repo, rev in to_fix.items():
self.printer.info(f" - {repo.repo}: {repo.rev} -> {rev}")
self.printer.list_updated_packages(
{mapping_reverse_by_url[repo.repo]: (repo, new_ver) for repo, new_ver in to_fix.items()}
)

if self.dry_run:
self.printer.info("Dry run, skipping pre-commit hook update.")
return
pre_commit_config_data.update_pre_commit_repo_versions(to_fix)
self.printer.success("Pre-commit hooks have been updated to match the lockfile!")
self.printer.success(f"Pre-commit hooks have been updated in {self.pre_commit_config_file_path.name}!")

def get_pre_commit_repo_new_version(
self,
Expand Down Expand Up @@ -110,8 +116,9 @@ def analyze_repos(
pre_commit_repos: set[PreCommitRepo],
mapping: PackageRepoMapping,
mapping_reverse_by_url: dict[str, str],
) -> dict[PreCommitRepo, str]:
) -> tuple[dict[PreCommitRepo, str], dict[PreCommitRepo, str]]:
to_fix: dict[PreCommitRepo, str] = {}
in_sync: dict[PreCommitRepo, str] = {}
for pre_commit_repo in pre_commit_repos:
if pre_commit_repo.repo not in mapping_reverse_by_url:
self.printer.debug(f"Pre-commit hook {pre_commit_repo.repo} not found in the DB mapping")
Expand All @@ -122,14 +129,16 @@ def analyze_repos(
dependency_locked = self.locked_packages.get(dependency_name)

if not dependency_locked:
self.printer.info(
f"Pre-commit hook {pre_commit_repo.repo} has a mapping to Python package `{dependency_name}`,"
self.printer.debug(
f"Pre-commit hook {pre_commit_repo.repo} has a mapping to Python package `{dependency_name}`, "
"but was not found in the lockfile"
)
continue

new_ver = self.get_pre_commit_repo_new_version(pre_commit_repo, dependency, dependency_locked)
if new_ver:
to_fix[pre_commit_repo] = new_ver
else:
in_sync[pre_commit_repo] = dependency_locked.version

return to_fix
return to_fix, in_sync
24 changes: 24 additions & 0 deletions src/sync_pre_commit_lock/pdm_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
from pdm.project import Project
from pdm.termui import UI

from sync_pre_commit_lock.pre_commit_config import PreCommitRepo


class PDMPrinter(Printer):
success_list_token: str = f"[success]{termui.Emoji.SUCC}[/]"
Expand Down Expand Up @@ -53,6 +55,28 @@ def error(self, msg: str) -> None:
def success(self, msg: str) -> None:
self.ui.echo("[success]" + self.prefix_lines(msg) + "[/success]", verbosity=Verbosity.NORMAL)

def _format_repo_url(self, repo_url: str, package_name: str) -> str:
return repo_url.replace(package_name, f"[cyan][bold]{package_name}[/bold][/cyan]")

def list_updated_packages(self, packages: dict[str, tuple[PreCommitRepo, str]]) -> None:
"""
Args:
packages: Dict of package name -> (repo, new_rev)
"""
self.ui.display_columns(
[
(
"[info]" + self.plugin_prefix + "[/info]" + " " + self.success_list_token,
"[info]" + self._format_repo_url(repo[0].repo, package) + "[/info]",
" ",
"[error]" + repo[0].rev + "[/error]",
"[info]" + "->" + "[/info]",
"[green]" + repo[1] + "[/green]",
)
for package, repo in packages.items()
]
)


def register_pdm_plugin(core: Core) -> None:
"""Register the plugin to PDM Core."""
Expand Down
26 changes: 26 additions & 0 deletions src/sync_pre_commit_lock/poetry_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
from cleo.events.event_dispatcher import EventDispatcher
from cleo.io.io import IO

from sync_pre_commit_lock.pre_commit_config import PreCommitRepo


class PoetryPrinter(Printer):
success_list_token: str = "<fg=green;options=bold>•</>"
Expand All @@ -52,6 +54,30 @@ def error(self, msg: str) -> None:
def success(self, msg: str) -> None:
return self.io.write_line(f"<success>{self.plugin_prefix} {msg}</success>", verbosity=Verbosity.NORMAL)

def list_updated_packages(self, packages: dict[str, tuple[PreCommitRepo, str]]) -> None:
from cleo.ui.table import Table

table = Table(self.io, style="compact")

table.set_rows(
[
[
"<info>" + self.plugin_prefix + " " + self.success_list_token,
self._format_repo_url(repo[0].repo, package),
" ",
"<warning>" + repo[0].rev + "</>",
"->",
"<success>" + repo[1] + "</>" + "</>",
]
for package, repo in packages.items()
]
)

table.render()

def _format_repo_url(self, repo_url: str, package_name: str) -> str:
return repo_url.replace(package_name, f"<c1>{package_name}</>")


class PoetrySetupPreCommitHooks(SetupPreCommitHooks):
install_pre_commit_hooks_command: ClassVar[Sequence[str | bytes]] = ["poetry", "run", "pre-commit", "install"]
Expand Down
25 changes: 13 additions & 12 deletions tests/test_actions/test_sync_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def test_execute_returns_early_during_dry_run(
# Mocks
pre_commit_config = MagicMock(spec=PreCommitHookConfig)
mock_from_yaml_file.return_value = pre_commit_config
mock_build_mapping.return_value = ({}, {})
mock_analyze_repos.return_value = {PreCommitRepo("repo1", "rev1"): "rev2"}
mock_build_mapping.return_value = ({}, {"repo1": "somepkg"})
mock_analyze_repos.return_value = {PreCommitRepo("repo1", "rev1"): "rev2"}, {}

syncer.execute()

Expand Down Expand Up @@ -116,6 +116,7 @@ def test_execute_synchronizes_hooks(
) -> None:
printer = MagicMock(spec=Printer)
pre_commit_config_file_path = MagicMock(spec=Path)
pre_commit_config_file_path.name = ".pre-commit-config.yaml"
locked_packages: dict[str, GenericLockedPackage] = {}
plugin_config = MagicMock(spec=SyncPreCommitLockConfig)
plugin_config.disable_sync_from_lock = False
Expand All @@ -132,22 +133,22 @@ def test_execute_synchronizes_hooks(
# Mocks
pre_commit_config = MagicMock(spec=PreCommitHookConfig)
mock_from_yaml_file.return_value = pre_commit_config
mock_build_mapping.return_value = ({}, {})
mock_analyze_repos.return_value = {PreCommitRepo("repo1", "rev1"): "rev2"}
mock_build_mapping.return_value = ({}, {"repo1": "somepkg"})
mock_analyze_repos.return_value = {PreCommitRepo("repo1", "rev1"): "rev2"}, {}

syncer.execute()

# Assertions
mock_build_mapping.assert_called_once()
mock_analyze_repos.assert_called_once()
pre_commit_config.update_pre_commit_repo_versions.assert_called_once_with({PreCommitRepo("repo1", "rev1"): "rev2"})
printer.success.assert_called_with("Pre-commit hooks have been updated to match the lockfile!")
printer.success.assert_called_with("Pre-commit hooks have been updated in .pre-commit-config.yaml!")


@patch("sync_pre_commit_lock.pre_commit_config.PreCommitHookConfig.from_yaml_file")
@patch.object(SyncPreCommitHooksVersion, "build_mapping")
@patch.object(SyncPreCommitHooksVersion, "analyze_repos")
def test_execute_synchronizes_hooks_all_good(
def test_execute_synchronizes_hooks_no_match(
mock_analyze_repos: MagicMock, mock_build_mapping: MagicMock, mock_from_yaml_file: MagicMock
) -> None:
printer = MagicMock(spec=Printer)
Expand All @@ -169,15 +170,15 @@ def test_execute_synchronizes_hooks_all_good(
pre_commit_config = MagicMock(spec=PreCommitHookConfig)
mock_from_yaml_file.return_value = pre_commit_config
mock_build_mapping.return_value = ({}, {})
mock_analyze_repos.return_value = {}
mock_analyze_repos.return_value = {}, {}

syncer.execute()

# Assertions
mock_build_mapping.assert_called_once()
mock_analyze_repos.assert_called_once()
pre_commit_config.update_pre_commit_repo_versions.assert_not_called()
printer.info.assert_called_with("All matched pre-commit hooks already in sync with the lockfile!")
printer.info.assert_called_with("No pre-commit hook detected that matches a locked package.")


def test_get_pre_commit_repo_new_version() -> None:
Expand Down Expand Up @@ -220,7 +221,7 @@ def test_analyze_repos(mock_get_pre_commit_repo_new_version: MagicMock) -> None:
mapping: PackageRepoMapping = {"lib_name": {"repo": "https://repo_url", "rev": "${rev}"}}
mapping_reverse_by_url = {"https://repo_url": "lib_name"}

to_fix = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)
to_fix, _ = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)

assert to_fix == {PreCommitRepo("https://repo_url", "1.2.3"): "2.0.0"}

Expand Down Expand Up @@ -310,7 +311,7 @@ def test_analyze_repos_repo_not_in_mapping() -> None:
mapping: PackageRepoMapping = {}
mapping_reverse_by_url: dict[str, str] = {}

result = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)
result, _ = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)

assert result == {}

Expand All @@ -332,7 +333,7 @@ def test_analyze_repos_dependency_not_locked() -> None:
mapping: PackageRepoMapping = {"lib_name": {"repo": "repo_url", "rev": "${rev}"}}
mapping_reverse_by_url = {"repo_url": "lib_name"}

result = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)
result, _ = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)

assert result == {}

Expand All @@ -355,6 +356,6 @@ def test_analyze_repos_no_new_version() -> None:
mapping = {"lib_name": RepoInfo(repo="repo_url", rev="${rev}")}
mapping_reverse_by_url = {"repo_url": "lib_name"}

result = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)
result, _ = syncer.analyze_repos(pre_commit_repos, mapping, mapping_reverse_by_url)

assert result == {}

0 comments on commit 74848d5

Please sign in to comment.