path-sync 0.7.4__tar.gz → 0.7.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {path_sync-0.7.4 → path_sync-0.7.5}/PKG-INFO +1 -1
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/__init__.py +1 -1
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/cmd_copy.py +6 -16
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/cmd_dep_update.py +4 -27
- path_sync-0.7.5/path_sync/_internal/repo_utils.py +37 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/pyproject.toml +1 -1
- {path_sync-0.7.4 → path_sync-0.7.5}/.gitignore +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/LICENSE +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/README.md +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/__main__.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/__init__.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/auto_merge.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/cmd_boot.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/cmd_options.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/cmd_validate.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/file_utils.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/git_ops.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/header.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/log_capture.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/models.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/models_dep.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/prompt_utils.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/typer_app.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/validation.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/verify.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/_internal/yaml_utils.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/config.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/copy.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/dep_update.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/sections.py +0 -0
- {path_sync-0.7.4 → path_sync-0.7.5}/path_sync/validate_no_changes.py +0 -0
|
@@ -23,6 +23,7 @@ from path_sync._internal.models import (
|
|
|
23
23
|
pr_already_synced,
|
|
24
24
|
resolve_config_path,
|
|
25
25
|
)
|
|
26
|
+
from path_sync._internal.repo_utils import ensure_repo, resolve_repo_path
|
|
26
27
|
from path_sync._internal.typer_app import app
|
|
27
28
|
from path_sync._internal.verify import StepFailure, VerifyResult, VerifyStatus
|
|
28
29
|
from path_sync._internal.yaml_utils import load_yaml_model
|
|
@@ -57,6 +58,7 @@ class CopyOptions(BaseModel):
|
|
|
57
58
|
skip_verify: bool = False
|
|
58
59
|
no_wait: bool = False
|
|
59
60
|
no_auto_merge: bool = False
|
|
61
|
+
work_dir: str = ""
|
|
60
62
|
pr_title: str = ""
|
|
61
63
|
labels: list[str] | None = None
|
|
62
64
|
reviewers: list[str] | None = None
|
|
@@ -78,6 +80,7 @@ def copy(
|
|
|
78
80
|
help="Source repo root (default: find git root from cwd)",
|
|
79
81
|
),
|
|
80
82
|
dest_filter: str = typer.Option("", "-d", "--dest", help="Filter destinations (comma-separated)"),
|
|
83
|
+
work_dir: str = typer.Option("", "--work-dir", help="Clone repos here (overrides dest_path_relative)"),
|
|
81
84
|
dry_run: bool = typer.Option(False, "--dry-run", help="Preview without writing"),
|
|
82
85
|
force_overwrite: bool = typer.Option(
|
|
83
86
|
False,
|
|
@@ -174,6 +177,7 @@ def copy(
|
|
|
174
177
|
skip_verify=skip_verify,
|
|
175
178
|
no_wait=no_wait,
|
|
176
179
|
no_auto_merge=no_auto_merge,
|
|
180
|
+
work_dir=work_dir,
|
|
177
181
|
pr_title=pr_title or config.pr_defaults.title,
|
|
178
182
|
labels=cmd_options.split_csv(pr_labels) or config.pr_defaults.labels,
|
|
179
183
|
reviewers=cmd_options.split_csv(pr_reviewers) or config.pr_defaults.reviewers,
|
|
@@ -251,12 +255,8 @@ def _sync_destination(
|
|
|
251
255
|
opts: CopyOptions,
|
|
252
256
|
read_log: Callable[[], str],
|
|
253
257
|
) -> tuple[int, PRRef | None]:
|
|
254
|
-
dest_root = (src_root
|
|
255
|
-
|
|
256
|
-
if opts.dry_run and not dest_root.exists():
|
|
257
|
-
raise ValueError(f"Destination repo not found: {dest_root}. Clone it first or run without --dry-run.")
|
|
258
|
-
|
|
259
|
-
dest_repo = _ensure_dest_repo(dest, dest_root, opts.dry_run)
|
|
258
|
+
dest_root = resolve_repo_path(dest, src_root, opts.work_dir)
|
|
259
|
+
dest_repo = ensure_repo(dest, dest_root, dry_run=opts.dry_run)
|
|
260
260
|
copy_branch = dest.resolved_copy_branch(config.name)
|
|
261
261
|
|
|
262
262
|
if _skip_already_synced(dest.name, dest_root, copy_branch, commit_ts, opts, config):
|
|
@@ -314,16 +314,6 @@ def _print_sync_summary(dest: Destination, result: SyncResult) -> None:
|
|
|
314
314
|
typer.echo(f"\n{result.total} changes ready.", err=True)
|
|
315
315
|
|
|
316
316
|
|
|
317
|
-
def _ensure_dest_repo(dest: Destination, dest_root: Path, dry_run: bool):
|
|
318
|
-
if not dest_root.exists():
|
|
319
|
-
if dry_run:
|
|
320
|
-
raise ValueError(f"Destination repo not found: {dest_root}. Clone it first or run without --dry-run.")
|
|
321
|
-
if not dest.repo_url:
|
|
322
|
-
raise ValueError(f"Dest {dest.name} not found and no repo_url configured")
|
|
323
|
-
git_ops.clone_repo(dest.repo_url, dest_root)
|
|
324
|
-
return git_ops.get_repo(dest_root)
|
|
325
|
-
|
|
326
|
-
|
|
327
317
|
def _sync_paths(
|
|
328
318
|
config: SrcConfig,
|
|
329
319
|
dest: Destination,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import shutil
|
|
5
4
|
import subprocess
|
|
6
5
|
from dataclasses import dataclass, field
|
|
7
6
|
from enum import StrEnum
|
|
@@ -10,7 +9,7 @@ from pathlib import Path
|
|
|
10
9
|
import typer
|
|
11
10
|
from git import Repo
|
|
12
11
|
|
|
13
|
-
from path_sync._internal import cmd_options, git_ops,
|
|
12
|
+
from path_sync._internal import cmd_options, git_ops, verify
|
|
14
13
|
from path_sync._internal.auto_merge import PRRef, handle_auto_merge
|
|
15
14
|
from path_sync._internal.log_capture import capture_log
|
|
16
15
|
from path_sync._internal.models import Destination, OnFailStrategy, find_repo_root
|
|
@@ -19,6 +18,7 @@ from path_sync._internal.models_dep import (
|
|
|
19
18
|
UpdateEntry,
|
|
20
19
|
resolve_dep_config_path,
|
|
21
20
|
)
|
|
21
|
+
from path_sync._internal.repo_utils import ensure_repo, resolve_repo_path
|
|
22
22
|
from path_sync._internal.typer_app import app
|
|
23
23
|
from path_sync._internal.verify import StepFailure, VerifyStatus
|
|
24
24
|
from path_sync._internal.yaml_utils import load_yaml_model
|
|
@@ -154,8 +154,8 @@ def _process_single_repo_inner(
|
|
|
154
154
|
opts: DepUpdateOptions,
|
|
155
155
|
) -> RepoResult:
|
|
156
156
|
logger.info(f"Processing {dest.name}...")
|
|
157
|
-
repo_path =
|
|
158
|
-
repo =
|
|
157
|
+
repo_path = resolve_repo_path(dest, src_root, work_dir)
|
|
158
|
+
repo = ensure_repo(dest, repo_path)
|
|
159
159
|
git_ops.prepare_copy_branch(repo, dest.default_branch, config.pr.branch, from_default=True)
|
|
160
160
|
|
|
161
161
|
if failure := _run_updates(config.updates, repo_path):
|
|
@@ -219,29 +219,6 @@ def _create_prs(config: DepConfig, results: list[RepoResult], opts: DepUpdateOpt
|
|
|
219
219
|
return pr_refs
|
|
220
220
|
|
|
221
221
|
|
|
222
|
-
def _resolve_repo_path(dest: Destination, src_root: Path, work_dir: str) -> Path:
|
|
223
|
-
if work_dir:
|
|
224
|
-
return Path(work_dir) / dest.name
|
|
225
|
-
if dest.dest_path_relative:
|
|
226
|
-
return (src_root / dest.dest_path_relative).resolve()
|
|
227
|
-
raise typer.BadParameter(f"No dest_path_relative for {dest.name}, --work-dir required")
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def _ensure_repo(dest: Destination, repo_path: Path, default_branch: str) -> Repo:
|
|
231
|
-
if repo_path.exists():
|
|
232
|
-
if git_ops.is_git_repo(repo_path):
|
|
233
|
-
repo = git_ops.get_repo(repo_path)
|
|
234
|
-
git_ops.fetch_and_reset_to_default(repo, default_branch)
|
|
235
|
-
return repo
|
|
236
|
-
logger.warning(f"Invalid git repo at {repo_path}")
|
|
237
|
-
if not prompt_utils.prompt_confirm(f"Remove {repo_path} and re-clone?"):
|
|
238
|
-
raise typer.Abort()
|
|
239
|
-
shutil.rmtree(repo_path)
|
|
240
|
-
if not dest.repo_url:
|
|
241
|
-
raise ValueError(f"Dest {dest.name} not found at {repo_path} and no repo_url configured")
|
|
242
|
-
return git_ops.clone_repo(dest.repo_url, repo_path)
|
|
243
|
-
|
|
244
|
-
|
|
245
222
|
def _build_pr_body(log_content: str, failures: list[StepFailure]) -> str:
|
|
246
223
|
body = "Automated dependency update."
|
|
247
224
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from git import Repo
|
|
8
|
+
|
|
9
|
+
from path_sync._internal import git_ops, prompt_utils
|
|
10
|
+
from path_sync._internal.models import Destination
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def resolve_repo_path(dest: Destination, src_root: Path, work_dir: str) -> Path:
|
|
16
|
+
if work_dir:
|
|
17
|
+
return Path(work_dir) / dest.name
|
|
18
|
+
if dest.dest_path_relative:
|
|
19
|
+
return (src_root / dest.dest_path_relative).resolve()
|
|
20
|
+
raise ValueError(f"No dest_path_relative for {dest.name}, use --work-dir")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def ensure_repo(dest: Destination, repo_path: Path, dry_run: bool = False) -> Repo:
|
|
24
|
+
if repo_path.exists():
|
|
25
|
+
if git_ops.is_git_repo(repo_path):
|
|
26
|
+
return git_ops.get_repo(repo_path)
|
|
27
|
+
logger.warning(f"Invalid git repo at {repo_path}")
|
|
28
|
+
if dry_run:
|
|
29
|
+
raise ValueError(f"Invalid git repo at {repo_path}, cannot re-clone in dry-run mode")
|
|
30
|
+
if not prompt_utils.prompt_confirm(f"Remove {repo_path} and re-clone?"):
|
|
31
|
+
raise ValueError(f"Aborted: user declined to remove invalid repo at {repo_path}")
|
|
32
|
+
shutil.rmtree(repo_path)
|
|
33
|
+
if dry_run:
|
|
34
|
+
raise ValueError(f"Destination repo not found: {repo_path}. Clone it first or run without --dry-run.")
|
|
35
|
+
if not dest.repo_url:
|
|
36
|
+
raise ValueError(f"Dest {dest.name} not found at {repo_path} and no repo_url configured")
|
|
37
|
+
return git_ops.clone_repo(dest.repo_url, repo_path)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|