github-forker 1.0.1__py3-none-any.whl
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.
- github_forker-1.0.1.dist-info/METADATA +384 -0
- github_forker-1.0.1.dist-info/RECORD +8 -0
- github_forker-1.0.1.dist-info/WHEEL +4 -0
- github_forker-1.0.1.dist-info/licenses/LICENSE +21 -0
- pygithub_fork/__init__.py +58 -0
- pygithub_fork/exceptions.py +39 -0
- pygithub_fork/forker.py +1000 -0
- pygithub_fork/models.py +117 -0
pygithub_fork/models.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pygithub_fork.models
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Dataclasses and enums for fork operations.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Callable, Optional, Union
|
|
11
|
+
|
|
12
|
+
from github.Repository import Repository
|
|
13
|
+
from github.Organization import Organization
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ForkStatus(Enum):
|
|
17
|
+
PENDING = "pending" # Submitted but not yet confirmed
|
|
18
|
+
CREATED = "created" # API call succeeded; readiness unconfirmed
|
|
19
|
+
ALREADY_EXISTED = "already_existed" # Idempotent re-run; fork pre-existed
|
|
20
|
+
READY = "ready" # Fork is fully populated and usable
|
|
21
|
+
TIMED_OUT_WAITING = "timed_out_waiting" # Created but readiness unconfirmed within timeout
|
|
22
|
+
FAILED = "failed" # Unrecoverable error
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class ForkRequest:
|
|
27
|
+
"""Declarative description of a single fork operation for use with fork_many / pool."""
|
|
28
|
+
source: Union[str, Repository]
|
|
29
|
+
organization: Optional[Union[str, Organization]] = None
|
|
30
|
+
name: Optional[str] = None
|
|
31
|
+
default_branch_only: Optional[bool] = None
|
|
32
|
+
# Per-request post-fork actions (override global config when set)
|
|
33
|
+
add_upstream_remote: Optional[bool] = None # git remote add upstream <clone_url>
|
|
34
|
+
local_path: Optional[str] = None # path to local clone for remote setup
|
|
35
|
+
register_webhook: Optional[bool] = None # register push/fork events webhook
|
|
36
|
+
webhook_url: Optional[str] = None # target URL for webhook
|
|
37
|
+
webhook_events: Optional[list[str]] = None # e.g. ["push", "fork"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class ForkResult:
|
|
42
|
+
"""Outcome of a single fork operation."""
|
|
43
|
+
source_full_name: str
|
|
44
|
+
fork: Optional[Repository]
|
|
45
|
+
status: ForkStatus
|
|
46
|
+
already_existed: bool
|
|
47
|
+
attempts: int = 1
|
|
48
|
+
elapsed_seconds: float = 0.0
|
|
49
|
+
error: Optional[Exception] = None
|
|
50
|
+
upstream_remote_added: bool = False
|
|
51
|
+
webhook_id: Optional[int] = None # GitHub hook ID if webhook was registered
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def succeeded(self) -> bool:
|
|
55
|
+
return self.fork is not None and self.error is None
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def clone_url(self) -> Optional[str]:
|
|
59
|
+
return self.fork.clone_url if self.fork else None
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def ssh_url(self) -> Optional[str]:
|
|
63
|
+
return self.fork.ssh_url if self.fork else None
|
|
64
|
+
|
|
65
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
66
|
+
return (
|
|
67
|
+
f"ForkResult(source={self.source_full_name!r}, "
|
|
68
|
+
f"status={self.status.value}, "
|
|
69
|
+
f"fork={self.fork.full_name if self.fork else None!r}, "
|
|
70
|
+
f"elapsed={self.elapsed_seconds:.2f}s)"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class ForkerConfig:
|
|
76
|
+
"""
|
|
77
|
+
Tunable behavior for GitHubForker. All fields have sensible production
|
|
78
|
+
defaults; override only what you need.
|
|
79
|
+
"""
|
|
80
|
+
# ---- Retry / rate-limit ----
|
|
81
|
+
max_retries: int = 5
|
|
82
|
+
base_backoff_seconds: float = 1.5
|
|
83
|
+
max_backoff_seconds: float = 60.0
|
|
84
|
+
|
|
85
|
+
# ---- Fork readiness polling ----
|
|
86
|
+
wait_for_ready: bool = True
|
|
87
|
+
ready_timeout_seconds: float = 90.0
|
|
88
|
+
ready_poll_interval_seconds: float = 3.0
|
|
89
|
+
|
|
90
|
+
# ---- Thread pool (fork_many / fork_async) ----
|
|
91
|
+
pool_workers: int = 4
|
|
92
|
+
"""Max concurrent forks. GitHub secondary rate limits kick in when you
|
|
93
|
+
fork too many repos in rapid succession; keep this ≤ 4 for safety."""
|
|
94
|
+
|
|
95
|
+
# ---- Post-fork: upstream remote ----
|
|
96
|
+
add_upstream_remote: bool = False
|
|
97
|
+
"""If True, runs `git remote add upstream <source_clone_url>` in
|
|
98
|
+
local_clone_path after a successful fork."""
|
|
99
|
+
local_clone_path: Optional[str] = None
|
|
100
|
+
"""Absolute path of the local clone where the upstream remote is added.
|
|
101
|
+
Only used when add_upstream_remote=True."""
|
|
102
|
+
|
|
103
|
+
# ---- Post-fork: webhook ----
|
|
104
|
+
register_webhook: bool = False
|
|
105
|
+
"""If True, registers a webhook on the freshly created fork."""
|
|
106
|
+
webhook_url: Optional[str] = None
|
|
107
|
+
"""Payload URL for the webhook."""
|
|
108
|
+
webhook_events: list[str] = field(default_factory=lambda: ["push", "fork"])
|
|
109
|
+
webhook_content_type: str = "json"
|
|
110
|
+
webhook_secret: Optional[str] = None
|
|
111
|
+
webhook_insecure_ssl: bool = False
|
|
112
|
+
|
|
113
|
+
# ---- Callbacks ----
|
|
114
|
+
on_retry: Optional[Callable[[int, Exception, float], None]] = None
|
|
115
|
+
"""Called as on_retry(attempt, exc, sleep_seconds) before each retry sleep."""
|
|
116
|
+
on_fork_done: Optional[Callable[["ForkResult"], None]] = None
|
|
117
|
+
"""Called after each successful (or failed) fork when using fork_many/pool."""
|