Skip to content

Commit e8eeaaa

Browse files
committed
cli/sync(fix[path-privacy]): redact success log paths
why: the human-mode "Synced ... -> PATH" line still printed absolute repo directories even though structured events were redacted. what: - compute a display_repo_path once per repo and use it for both JSON payloads and human logs - add a focused sync test that stubs the data pipeline and asserts the stdout text contains the tilde-collapsed path
1 parent 2b0c837 commit e8eeaaa

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

src/vcspull/cli/sync.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,13 +718,14 @@ def silent_progress(output: str, timestamp: datetime) -> None:
718718
repo_name = repo.get("name", "unknown")
719719
repo_path = repo.get("path", "unknown")
720720
workspace_label = repo.get("workspace_root", "")
721+
display_repo_path = str(PrivatePath(repo_path))
721722

722723
summary["total"] += 1
723724

724725
event: dict[str, t.Any] = {
725726
"reason": "sync",
726727
"name": repo_name,
727-
"path": str(PrivatePath(repo_path)),
728+
"path": display_repo_path,
728729
"workspace_root": str(workspace_label),
729730
}
730731

@@ -783,7 +784,7 @@ def silent_progress(output: str, timestamp: datetime) -> None:
783784
formatter.emit(event)
784785
formatter.emit_text(
785786
f"{colors.success('✓')} Synced {colors.info(repo_name)} "
786-
f"{colors.muted('→')} {repo_path}",
787+
f"{colors.muted('→')} {display_repo_path}",
787788
)
788789

789790
formatter.emit(

tests/test_cli.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import yaml
1515

1616
from vcspull.__about__ import __version__
17+
from vcspull._internal.private_path import PrivatePath
1718
from vcspull.cli import cli
1819
from vcspull.cli._output import PlanAction, PlanEntry, PlanResult, PlanSummary
1920
from vcspull.cli.sync import EXIT_ON_ERROR_MSG, NO_REPOS_FOR_TERM_MSG
@@ -974,3 +975,75 @@ def test_sync_dry_run_plan_progress(
974975
output = f"{captured.out}{captured.err}"
975976
assert "Progress:" in output
976977
assert "Plan:" in output
978+
979+
980+
def test_sync_human_output_redacts_repo_paths(
981+
capsys: pytest.CaptureFixture[str],
982+
monkeypatch: pytest.MonkeyPatch,
983+
user_path: pathlib.Path,
984+
) -> None:
985+
"""Synced log lines should collapse repo paths via PrivatePath."""
986+
repo_path = user_path / "repos" / "private"
987+
repo_path.mkdir(parents=True, exist_ok=True)
988+
989+
repo_config = {
990+
"name": "private",
991+
"url": "git+https://example.com/private.git",
992+
"path": str(repo_path),
993+
"workspace_root": "~/repos/",
994+
}
995+
996+
monkeypatch.setattr(
997+
sync_module,
998+
"load_configs",
999+
lambda _paths: [repo_config],
1000+
)
1001+
monkeypatch.setattr(
1002+
sync_module,
1003+
"find_config_files",
1004+
lambda include_home=True: [],
1005+
)
1006+
1007+
def _fake_filter_repos(
1008+
_configs: list[dict[str, t.Any]],
1009+
*,
1010+
path: str | None = None,
1011+
vcs_url: str | None = None,
1012+
name: str | None = None,
1013+
) -> list[dict[str, t.Any]]:
1014+
if name and name != repo_config["name"]:
1015+
return []
1016+
if path and path != repo_config["path"]:
1017+
return []
1018+
if vcs_url and vcs_url != repo_config["url"]:
1019+
return []
1020+
return [repo_config]
1021+
1022+
monkeypatch.setattr(sync_module, "filter_repos", _fake_filter_repos)
1023+
monkeypatch.setattr(
1024+
sync_module,
1025+
"update_repo",
1026+
lambda _repo, progress_callback=None: None,
1027+
)
1028+
1029+
sync_module.sync(
1030+
repo_patterns=[repo_config["name"]],
1031+
config=None,
1032+
workspace_root=None,
1033+
dry_run=False,
1034+
output_json=False,
1035+
output_ndjson=False,
1036+
color="never",
1037+
exit_on_error=False,
1038+
show_unchanged=False,
1039+
summary_only=False,
1040+
long_view=False,
1041+
relative_paths=False,
1042+
fetch=False,
1043+
offline=False,
1044+
verbosity=0,
1045+
)
1046+
1047+
captured = capsys.readouterr()
1048+
expected_path = str(PrivatePath(repo_path))
1049+
assert expected_path in captured.out

0 commit comments

Comments
 (0)