|
| 1 | +from fnmatch import translate as glob_translate |
| 2 | +from pathlib import Path |
| 3 | + |
1 | 4 | import click |
| 5 | +from pydantic import BaseModel, ConfigDict, FilePath, field_validator |
2 | 6 |
|
3 | 7 | from git_sync_filtered.sync import sync |
4 | 8 |
|
5 | 9 |
|
| 10 | +class SyncConfig(BaseModel): |
| 11 | + model_config = ConfigDict(frozen=True) |
| 12 | + |
| 13 | + private: str |
| 14 | + public: str |
| 15 | + keep: tuple[str, ...] |
| 16 | + keep_from_file: FilePath | None = None |
| 17 | + sync_branch: str = "upstream/sync" |
| 18 | + main_branch: str = "main" |
| 19 | + private_branch: str = "main" |
| 20 | + dry_run: bool = False |
| 21 | + merge: bool = False |
| 22 | + force: bool = False |
| 23 | + |
| 24 | + @field_validator("keep", mode="before") |
| 25 | + @classmethod |
| 26 | + def ensure_non_empty(cls, v: tuple[str, ...]) -> tuple[str, ...]: |
| 27 | + if not v: |
| 28 | + raise ValueError("At least one --keep path required") |
| 29 | + return v |
| 30 | + |
| 31 | + @field_validator("keep", mode="after") |
| 32 | + @classmethod |
| 33 | + def validate_glob_paths(cls, v: tuple[str, ...]) -> tuple[str, ...]: |
| 34 | + for path in v: |
| 35 | + if not path: |
| 36 | + raise ValueError("Keep path cannot be empty") |
| 37 | + glob_translate(path) |
| 38 | + return v |
| 39 | + |
| 40 | + @field_validator("sync_branch", "main_branch", "private_branch", mode="after") |
| 41 | + @classmethod |
| 42 | + def validate_branch_name(cls, v: str) -> str: |
| 43 | + if not v: |
| 44 | + raise ValueError("Branch name cannot be empty") |
| 45 | + if v.startswith("/") or ".." in v: |
| 46 | + raise ValueError(f"Invalid branch name: {v!r}") |
| 47 | + return v |
| 48 | + |
| 49 | + |
6 | 50 | @click.command() |
7 | 51 | @click.option("--private", required=True, help="Private repo path or URL") |
8 | 52 | @click.option("--public", required=True, help="Public repo path or URL") |
@@ -35,18 +79,30 @@ def main( |
35 | 79 | """Sync filtered commits from private to public repository.""" |
36 | 80 |
|
37 | 81 | try: |
38 | | - result = sync( |
| 82 | + config = SyncConfig( |
39 | 83 | private=private, |
40 | 84 | public=public, |
41 | 85 | keep=keep, |
42 | | - keep_from_file=keep_from_file, |
| 86 | + keep_from_file=Path(keep_from_file) if keep_from_file else None, |
43 | 87 | sync_branch=sync_branch, |
44 | 88 | main_branch=main_branch, |
45 | 89 | private_branch=private_branch, |
46 | 90 | dry_run=dry_run, |
47 | 91 | merge=merge, |
48 | 92 | force=force, |
49 | 93 | ) |
| 94 | + result = sync( |
| 95 | + private=config.private, |
| 96 | + public=config.public, |
| 97 | + keep=config.keep, |
| 98 | + keep_from_file=config.keep_from_file, |
| 99 | + sync_branch=config.sync_branch, |
| 100 | + main_branch=config.main_branch, |
| 101 | + private_branch=config.private_branch, |
| 102 | + dry_run=config.dry_run, |
| 103 | + merge=config.merge, |
| 104 | + force=config.force, |
| 105 | + ) |
50 | 106 | except ValueError as e: |
51 | 107 | raise click.ClickException(str(e)) |
52 | 108 |
|
|
0 commit comments