pull-request-fixer 0.1.0__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.
- pull_request_fixer/__init__.py +13 -0
- pull_request_fixer/_version.py +34 -0
- pull_request_fixer/cli.py +1059 -0
- pull_request_fixer/exceptions.py +66 -0
- pull_request_fixer/github_client.py +330 -0
- pull_request_fixer/graphql_queries.py +310 -0
- pull_request_fixer/models.py +70 -0
- pull_request_fixer/pr_fixer.py +37 -0
- pull_request_fixer/pr_scanner.py +423 -0
- pull_request_fixer/progress_tracker.py +483 -0
- pull_request_fixer/py.typed +0 -0
- pull_request_fixer-0.1.0.dist-info/METADATA +451 -0
- pull_request_fixer-0.1.0.dist-info/RECORD +16 -0
- pull_request_fixer-0.1.0.dist-info/WHEEL +4 -0
- pull_request_fixer-0.1.0.dist-info/entry_points.txt +2 -0
- pull_request_fixer-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: 2025 The Linux Foundation
|
|
3
|
+
|
|
4
|
+
from contextlib import suppress
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.live import Live
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
RICH_AVAILABLE = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
RICH_AVAILABLE = False
|
|
16
|
+
|
|
17
|
+
# Fallback classes for when Rich is not available
|
|
18
|
+
class Live: # type: ignore
|
|
19
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def start(self) -> None:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def stop(self) -> None:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def update(self, *args: Any) -> None:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
class Text: # type: ignore
|
|
32
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def append(self, *args: Any, **kwargs: Any) -> None:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
class Console: # type: ignore
|
|
39
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ProgressTracker:
|
|
44
|
+
"""Real-time progress tracker for organization blocked PR checking operations."""
|
|
45
|
+
|
|
46
|
+
def __init__(self, organization: str, show_pr_stats: bool = True):
|
|
47
|
+
"""Initialize progress tracker for an organization blocked PR check.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
organization: Name of the GitHub organization being checked
|
|
51
|
+
show_pr_stats: Whether to show PR analysis statistics (default True)
|
|
52
|
+
"""
|
|
53
|
+
self.organization = organization
|
|
54
|
+
self.start_time = datetime.now()
|
|
55
|
+
self.console = Console() if RICH_AVAILABLE else None
|
|
56
|
+
|
|
57
|
+
# Progress counters
|
|
58
|
+
self.total_repositories = 0
|
|
59
|
+
self.completed_repositories = 0
|
|
60
|
+
self.current_repository = ""
|
|
61
|
+
self.total_prs_analyzed = 0
|
|
62
|
+
self.unmergeable_prs_found = 0
|
|
63
|
+
self.current_operation = "Initializing..."
|
|
64
|
+
self.errors_count = 0
|
|
65
|
+
|
|
66
|
+
# Configuration
|
|
67
|
+
self.show_pr_stats = show_pr_stats
|
|
68
|
+
|
|
69
|
+
# Rate limiting tracking
|
|
70
|
+
self.rate_limited = False
|
|
71
|
+
self.rate_limit_reset_time: datetime | None = None
|
|
72
|
+
|
|
73
|
+
# Rich Live display
|
|
74
|
+
self.live: Live | None = None
|
|
75
|
+
self.rich_available = RICH_AVAILABLE
|
|
76
|
+
self.paused = False
|
|
77
|
+
# Metrics (optional; displayed when provided)
|
|
78
|
+
self.metrics_concurrency: int | None = None
|
|
79
|
+
self.metrics_rps: float | None = None
|
|
80
|
+
|
|
81
|
+
# Fallback for when Rich is not available
|
|
82
|
+
self._last_display = ""
|
|
83
|
+
|
|
84
|
+
def start(self) -> None:
|
|
85
|
+
"""Start the live progress display."""
|
|
86
|
+
if not self.rich_available:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
self.live = Live(
|
|
91
|
+
self._generate_display_text(),
|
|
92
|
+
console=self.console,
|
|
93
|
+
refresh_per_second=2,
|
|
94
|
+
transient=False,
|
|
95
|
+
)
|
|
96
|
+
if self.live:
|
|
97
|
+
self.live.start()
|
|
98
|
+
except Exception:
|
|
99
|
+
# Fallback if Rich display fails (e.g., unsupported terminal)
|
|
100
|
+
self.rich_available = False
|
|
101
|
+
self.live = None
|
|
102
|
+
|
|
103
|
+
def stop(self) -> None:
|
|
104
|
+
"""Stop the live progress display."""
|
|
105
|
+
if self.live:
|
|
106
|
+
with suppress(Exception):
|
|
107
|
+
self.live.stop()
|
|
108
|
+
# Ensure paused state is cleared when fully stopped
|
|
109
|
+
self.paused = False
|
|
110
|
+
|
|
111
|
+
def suspend(self) -> None:
|
|
112
|
+
"""Temporarily pause the live display to allow clean printing elsewhere."""
|
|
113
|
+
if self.live and self.rich_available and not self.paused:
|
|
114
|
+
with suppress(Exception):
|
|
115
|
+
self.live.stop()
|
|
116
|
+
self.paused = True
|
|
117
|
+
|
|
118
|
+
def resume(self) -> None:
|
|
119
|
+
"""Resume the live display after it was suspended."""
|
|
120
|
+
if self.rich_available and self.paused:
|
|
121
|
+
with suppress(Exception):
|
|
122
|
+
self.live = Live(
|
|
123
|
+
self._generate_display_text(),
|
|
124
|
+
console=self.console,
|
|
125
|
+
refresh_per_second=2,
|
|
126
|
+
transient=False,
|
|
127
|
+
)
|
|
128
|
+
if self.live:
|
|
129
|
+
self.live.start()
|
|
130
|
+
self.paused = False
|
|
131
|
+
|
|
132
|
+
def update_metrics(self, concurrency: int, rps: float) -> None:
|
|
133
|
+
"""Update concurrency and RPS metrics (no-op since metrics are not displayed)."""
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
def clear_metrics(self) -> None:
|
|
137
|
+
"""Clear the concurrency and RPS metrics (no-op since metrics are not displayed)."""
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
def update_total_repositories(self, total: int) -> None:
|
|
141
|
+
"""Update the total number of repositories to process."""
|
|
142
|
+
self.total_repositories = total
|
|
143
|
+
self._refresh_display()
|
|
144
|
+
|
|
145
|
+
def start_repository(self, repo_name: str) -> None:
|
|
146
|
+
"""Mark the start of processing a repository."""
|
|
147
|
+
self.current_repository = repo_name
|
|
148
|
+
self.current_operation = f"Getting PRs from {repo_name}"
|
|
149
|
+
self._refresh_display()
|
|
150
|
+
|
|
151
|
+
def complete_repository(self, unmergeable_count: int = 0) -> None:
|
|
152
|
+
"""Mark completion of a repository."""
|
|
153
|
+
self.completed_repositories += 1
|
|
154
|
+
self.unmergeable_prs_found += unmergeable_count
|
|
155
|
+
self.current_operation = "Moving to next repository..."
|
|
156
|
+
self._refresh_display()
|
|
157
|
+
|
|
158
|
+
def update_operation(self, operation: str) -> None:
|
|
159
|
+
"""Update the current operation description."""
|
|
160
|
+
self.current_operation = operation
|
|
161
|
+
self._refresh_display()
|
|
162
|
+
|
|
163
|
+
def analyze_pr(self, pr_number: int, repo_name: str) -> None:
|
|
164
|
+
"""Mark the start of analyzing a specific PR."""
|
|
165
|
+
self.total_prs_analyzed += 1
|
|
166
|
+
self.current_operation = f"Analyzing PR #{pr_number} in {repo_name}"
|
|
167
|
+
self._refresh_display()
|
|
168
|
+
|
|
169
|
+
def add_error(self) -> None:
|
|
170
|
+
"""Increment the error count."""
|
|
171
|
+
self.errors_count += 1
|
|
172
|
+
self._refresh_display()
|
|
173
|
+
|
|
174
|
+
def set_rate_limited(self, reset_time: datetime) -> None:
|
|
175
|
+
"""Mark that we're rate limited and show countdown."""
|
|
176
|
+
self.rate_limited = True
|
|
177
|
+
self.rate_limit_reset_time = reset_time
|
|
178
|
+
self._refresh_display()
|
|
179
|
+
|
|
180
|
+
def clear_rate_limited(self) -> None:
|
|
181
|
+
"""Clear rate limiting status."""
|
|
182
|
+
self.rate_limited = False
|
|
183
|
+
self.rate_limit_reset_time = None
|
|
184
|
+
self._refresh_display()
|
|
185
|
+
|
|
186
|
+
def _refresh_display(self) -> None:
|
|
187
|
+
"""Refresh the live display if it's active."""
|
|
188
|
+
if self.live and self.rich_available and not self.paused:
|
|
189
|
+
with suppress(Exception):
|
|
190
|
+
self.live.update(self._generate_display_text())
|
|
191
|
+
elif not self.rich_available:
|
|
192
|
+
self._fallback_display()
|
|
193
|
+
|
|
194
|
+
def _generate_display_text(self) -> Any:
|
|
195
|
+
"""Generate the current progress display text."""
|
|
196
|
+
if not self.rich_available:
|
|
197
|
+
return Text()
|
|
198
|
+
|
|
199
|
+
text = Text()
|
|
200
|
+
|
|
201
|
+
# Main progress line
|
|
202
|
+
if self.total_repositories > 0:
|
|
203
|
+
progress_pct = (
|
|
204
|
+
self.completed_repositories / self.total_repositories
|
|
205
|
+
) * 100
|
|
206
|
+
text.append("š Checking ", style="bold blue")
|
|
207
|
+
text.append(f"{self.organization} ", style="bold cyan")
|
|
208
|
+
text.append(
|
|
209
|
+
f"({self.completed_repositories}/{self.total_repositories} repos, ",
|
|
210
|
+
style="white",
|
|
211
|
+
)
|
|
212
|
+
text.append(f"{progress_pct:.0f}%", style="green")
|
|
213
|
+
text.append(") | ", style="white")
|
|
214
|
+
else:
|
|
215
|
+
text.append("š Checking organization ", style="bold blue")
|
|
216
|
+
text.append(f"{self.organization}", style="bold cyan")
|
|
217
|
+
text.append(" (counting repositories...)", style="white")
|
|
218
|
+
|
|
219
|
+
# Stats (only when repo count is known and PR stats are enabled)
|
|
220
|
+
if self.total_repositories > 0 and self.show_pr_stats:
|
|
221
|
+
text.append(
|
|
222
|
+
f"{self.total_prs_analyzed} PRs analyzed | ", style="white"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
if self.unmergeable_prs_found > 0:
|
|
226
|
+
text.append(
|
|
227
|
+
f"{self.unmergeable_prs_found} unmergeable", style="red"
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
text.append(
|
|
231
|
+
f"{self.unmergeable_prs_found} unmergeable", style="green"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if self.errors_count > 0:
|
|
235
|
+
text.append(f" | {self.errors_count} errors", style="yellow")
|
|
236
|
+
elif self.total_repositories > 0 and not self.show_pr_stats:
|
|
237
|
+
# Show errors even when PR stats are disabled
|
|
238
|
+
if self.errors_count > 0:
|
|
239
|
+
text.append(f"{self.errors_count} errors", style="yellow")
|
|
240
|
+
|
|
241
|
+
text.append("\n")
|
|
242
|
+
|
|
243
|
+
# Current operation line
|
|
244
|
+
if self.rate_limited and self.rate_limit_reset_time:
|
|
245
|
+
remaining = self.rate_limit_reset_time - datetime.now()
|
|
246
|
+
if remaining.total_seconds() > 0:
|
|
247
|
+
text.append(
|
|
248
|
+
f"ā³ Rate limited - waiting {remaining.seconds}s",
|
|
249
|
+
style="yellow",
|
|
250
|
+
)
|
|
251
|
+
else:
|
|
252
|
+
text.append("ā” Rate limit reset - resuming...", style="green")
|
|
253
|
+
else:
|
|
254
|
+
text.append(f"š {self.current_operation}", style="dim white")
|
|
255
|
+
|
|
256
|
+
# Elapsed time
|
|
257
|
+
elapsed = datetime.now() - self.start_time
|
|
258
|
+
text.append(
|
|
259
|
+
f"\nā±ļø Elapsed: {self._format_duration(elapsed)}", style="dim blue"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return text
|
|
263
|
+
|
|
264
|
+
def _fallback_display(self) -> None:
|
|
265
|
+
"""Fallback display method for when Rich is not available."""
|
|
266
|
+
# Generate simple text display
|
|
267
|
+
if self.total_repositories > 0:
|
|
268
|
+
progress_pct = (
|
|
269
|
+
self.completed_repositories / self.total_repositories
|
|
270
|
+
) * 100
|
|
271
|
+
if self.show_pr_stats:
|
|
272
|
+
progress_line = f"š Checking {self.organization} ({self.completed_repositories}/{self.total_repositories} repos, {progress_pct:.0f}%) | {self.total_prs_analyzed} PRs analyzed | {self.unmergeable_prs_found} unmergeable"
|
|
273
|
+
else:
|
|
274
|
+
progress_line = f"š Checking {self.organization} ({self.completed_repositories}/{self.total_repositories} repos, {progress_pct:.0f}%)"
|
|
275
|
+
if self.errors_count > 0:
|
|
276
|
+
progress_line += f" | {self.errors_count} errors"
|
|
277
|
+
else:
|
|
278
|
+
progress_line = f"š Checking organization {self.organization} (counting repositories...)"
|
|
279
|
+
|
|
280
|
+
operation_line = f"š {self.current_operation}"
|
|
281
|
+
elapsed = datetime.now() - self.start_time
|
|
282
|
+
time_line = f"ā±ļø Elapsed: {self._format_duration(elapsed)}"
|
|
283
|
+
|
|
284
|
+
current_display = f"{progress_line}\n{operation_line}\n{time_line}"
|
|
285
|
+
|
|
286
|
+
# Only print if display has changed to avoid spam
|
|
287
|
+
if current_display != self._last_display:
|
|
288
|
+
print(f"\r{current_display}\n", end="", flush=True)
|
|
289
|
+
self._last_display = current_display
|
|
290
|
+
|
|
291
|
+
def _format_duration(self, duration: timedelta) -> str:
|
|
292
|
+
"""Format a duration for display."""
|
|
293
|
+
total_seconds = int(duration.total_seconds())
|
|
294
|
+
minutes = total_seconds // 60
|
|
295
|
+
seconds = total_seconds % 60
|
|
296
|
+
|
|
297
|
+
if minutes > 0:
|
|
298
|
+
return f"{minutes}m {seconds}s"
|
|
299
|
+
else:
|
|
300
|
+
return f"{seconds}s"
|
|
301
|
+
|
|
302
|
+
def get_summary(self) -> dict[str, Any]:
|
|
303
|
+
"""Get a summary of the checking progress."""
|
|
304
|
+
elapsed = datetime.now() - self.start_time
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
"organization": self.organization,
|
|
308
|
+
"total_repositories": self.total_repositories,
|
|
309
|
+
"completed_repositories": self.completed_repositories,
|
|
310
|
+
"total_prs_analyzed": self.total_prs_analyzed,
|
|
311
|
+
"unmergeable_prs_found": self.unmergeable_prs_found,
|
|
312
|
+
"errors_count": self.errors_count,
|
|
313
|
+
"elapsed_time": self._format_duration(elapsed),
|
|
314
|
+
"rate_limited": self.rate_limited,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class MergeProgressTracker(ProgressTracker):
|
|
319
|
+
"""Specialized progress tracker for merge operations."""
|
|
320
|
+
|
|
321
|
+
def __init__(self, organization: str, is_close_operation: bool = False):
|
|
322
|
+
super().__init__(organization)
|
|
323
|
+
self.similar_prs_found = 0
|
|
324
|
+
self.prs_merged = 0
|
|
325
|
+
self.merge_failures = 0
|
|
326
|
+
self.prs_closed = 0
|
|
327
|
+
self.is_close_operation = is_close_operation
|
|
328
|
+
|
|
329
|
+
def add_similar_pr(self) -> None:
|
|
330
|
+
"""Mark that a similar PR was found."""
|
|
331
|
+
self.similar_prs_found += 1
|
|
332
|
+
self._refresh_display()
|
|
333
|
+
|
|
334
|
+
def merge_success(self) -> None:
|
|
335
|
+
"""Mark a successful merge."""
|
|
336
|
+
self.prs_merged += 1
|
|
337
|
+
self._refresh_display()
|
|
338
|
+
|
|
339
|
+
def merge_failure(self) -> None:
|
|
340
|
+
"""Mark a failed merge."""
|
|
341
|
+
self.merge_failures += 1
|
|
342
|
+
self._refresh_display()
|
|
343
|
+
|
|
344
|
+
def increment_closed(self) -> None:
|
|
345
|
+
"""Mark a successful close."""
|
|
346
|
+
self.prs_closed += 1
|
|
347
|
+
self._refresh_display()
|
|
348
|
+
|
|
349
|
+
def _generate_display_text(self) -> Any:
|
|
350
|
+
"""Generate merge-specific display text."""
|
|
351
|
+
if not self.rich_available:
|
|
352
|
+
return Text()
|
|
353
|
+
|
|
354
|
+
text = Text()
|
|
355
|
+
|
|
356
|
+
# Main progress line for merge/close operations
|
|
357
|
+
if self.total_repositories > 0:
|
|
358
|
+
progress_pct = (
|
|
359
|
+
self.completed_repositories / self.total_repositories
|
|
360
|
+
) * 100
|
|
361
|
+
operation_icon = "šŖ" if self.is_close_operation else "š"
|
|
362
|
+
operation_text = (
|
|
363
|
+
"Searching for similar PRs"
|
|
364
|
+
if not self.is_close_operation
|
|
365
|
+
else "Searching for similar PRs"
|
|
366
|
+
)
|
|
367
|
+
text.append(
|
|
368
|
+
f"{operation_icon} {operation_text} ", style="bold blue"
|
|
369
|
+
)
|
|
370
|
+
text.append(
|
|
371
|
+
f"({self.completed_repositories}/{self.total_repositories} repos, ",
|
|
372
|
+
style="white",
|
|
373
|
+
)
|
|
374
|
+
text.append(f"{progress_pct:.0f}%", style="green")
|
|
375
|
+
text.append(") | ", style="white")
|
|
376
|
+
# Special heading when examining the source PR before repo enumeration
|
|
377
|
+
elif "Getting source PR details" in (self.current_operation or ""):
|
|
378
|
+
text.append(
|
|
379
|
+
"š Examining source pull request in ", style="bold blue"
|
|
380
|
+
)
|
|
381
|
+
text.append(f"{self.organization}", style="bold cyan")
|
|
382
|
+
else:
|
|
383
|
+
operation_icon = "šŖ" if self.is_close_operation else "š"
|
|
384
|
+
text.append(
|
|
385
|
+
f"{operation_icon} Analyzing PRs in ", style="bold blue"
|
|
386
|
+
)
|
|
387
|
+
text.append(f"{self.organization}", style="bold cyan")
|
|
388
|
+
|
|
389
|
+
# Stats for merge operations (only when repo count is known)
|
|
390
|
+
if self.total_repositories > 0:
|
|
391
|
+
text.append(
|
|
392
|
+
f"{self.total_prs_analyzed} PRs analyzed", style="white"
|
|
393
|
+
)
|
|
394
|
+
if self.errors_count > 0:
|
|
395
|
+
text.append(f" | {self.errors_count} errors", style="yellow")
|
|
396
|
+
|
|
397
|
+
text.append("\n")
|
|
398
|
+
|
|
399
|
+
# Current operation line
|
|
400
|
+
if self.rate_limited and self.rate_limit_reset_time:
|
|
401
|
+
remaining = self.rate_limit_reset_time - datetime.now()
|
|
402
|
+
if remaining.total_seconds() > 0:
|
|
403
|
+
text.append(
|
|
404
|
+
f"ā³ Rate limited - waiting {remaining.seconds}s",
|
|
405
|
+
style="yellow",
|
|
406
|
+
)
|
|
407
|
+
else:
|
|
408
|
+
text.append("ā” Rate limit reset - resuming...", style="green")
|
|
409
|
+
else:
|
|
410
|
+
text.append(f"š {self.current_operation}", style="dim white")
|
|
411
|
+
|
|
412
|
+
# Elapsed time
|
|
413
|
+
elapsed = datetime.now() - self.start_time
|
|
414
|
+
text.append(
|
|
415
|
+
f"\nā±ļø Elapsed: {self._format_duration(elapsed)}", style="dim blue"
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
return text
|
|
419
|
+
|
|
420
|
+
def get_summary(self) -> dict[str, Any]:
|
|
421
|
+
"""Get merge-specific summary."""
|
|
422
|
+
summary = super().get_summary()
|
|
423
|
+
summary.update(
|
|
424
|
+
{
|
|
425
|
+
"similar_prs_found": self.similar_prs_found,
|
|
426
|
+
"prs_merged": self.prs_merged,
|
|
427
|
+
"merge_failures": self.merge_failures,
|
|
428
|
+
"prs_closed": self.prs_closed,
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
return summary
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
class DummyProgressTracker:
|
|
435
|
+
"""A no-op progress tracker for when progress display is disabled."""
|
|
436
|
+
|
|
437
|
+
def __init__(self, organization: str):
|
|
438
|
+
self.organization = organization
|
|
439
|
+
|
|
440
|
+
def start(self) -> None:
|
|
441
|
+
pass
|
|
442
|
+
|
|
443
|
+
def stop(self) -> None:
|
|
444
|
+
pass
|
|
445
|
+
|
|
446
|
+
def update_total_repositories(self, total: int) -> None:
|
|
447
|
+
pass
|
|
448
|
+
|
|
449
|
+
def start_repository(self, repo_name: str) -> None:
|
|
450
|
+
pass
|
|
451
|
+
|
|
452
|
+
def complete_repository(self, unmergeable_count: int = 0) -> None:
|
|
453
|
+
pass
|
|
454
|
+
|
|
455
|
+
def update_operation(self, operation: str) -> None:
|
|
456
|
+
pass
|
|
457
|
+
|
|
458
|
+
def analyze_pr(self, pr_number: int, repo_name: str) -> None:
|
|
459
|
+
pass
|
|
460
|
+
|
|
461
|
+
def add_error(self) -> None:
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
def set_rate_limited(self, reset_time: datetime) -> None:
|
|
465
|
+
pass
|
|
466
|
+
|
|
467
|
+
def clear_rate_limited(self) -> None:
|
|
468
|
+
pass
|
|
469
|
+
|
|
470
|
+
def found_similar_pr(self) -> None:
|
|
471
|
+
pass
|
|
472
|
+
|
|
473
|
+
def merge_success(self) -> None:
|
|
474
|
+
pass
|
|
475
|
+
|
|
476
|
+
def merge_failure(self) -> None:
|
|
477
|
+
pass
|
|
478
|
+
|
|
479
|
+
def increment_closed(self) -> None:
|
|
480
|
+
pass
|
|
481
|
+
|
|
482
|
+
def get_summary(self) -> dict[str, Any]:
|
|
483
|
+
return {"organization": self.organization}
|
|
File without changes
|