|
1 | 1 | import os
|
2 | 2 | import json
|
3 |
| -from typing import Optional |
| 3 | +import re |
| 4 | +from typing import Optional, List |
4 | 5 | from github import Github
|
5 |
| -from pydantic import BaseModel |
| 6 | +from pydantic import BaseModel, Field |
6 | 7 | from .config_parser import Config
|
7 | 8 |
|
8 | 9 |
|
| 10 | +class LinkedItem(BaseModel): |
| 11 | + """Represents a linked GitHub issue or PR""" |
| 12 | + |
| 13 | + number: int |
| 14 | + title: str |
| 15 | + body: str |
| 16 | + labels: List[str] |
| 17 | + type: str = Field(..., pattern="^(issue|pull_request)$") |
| 18 | + |
| 19 | + |
9 | 20 | class PullRequest(BaseModel):
|
10 | 21 | title: str
|
11 | 22 | body: str
|
12 | 23 | files: dict[str, str | None]
|
13 | 24 | author: str # GitHub username
|
| 25 | + linked_items: List[LinkedItem] = Field(default_factory=list) |
14 | 26 |
|
15 | 27 |
|
16 | 28 | class Issue(BaseModel):
|
17 | 29 | title: str
|
18 | 30 | body: str
|
19 | 31 | author: str # GitHub username
|
| 32 | + linked_items: List[LinkedItem] = Field(default_factory=list) |
20 | 33 |
|
21 | 34 |
|
22 | 35 | class Label(BaseModel):
|
@@ -135,3 +148,48 @@ def get_available_labels_from_config(
|
135 | 148 | labels.append(label)
|
136 | 149 |
|
137 | 150 | return labels
|
| 151 | + |
| 152 | + |
| 153 | +def parse_github_links(text: str) -> List[int]: |
| 154 | + """Extract GitHub issue/PR numbers from text using common formats: |
| 155 | + - #123 |
| 156 | + - repo#123 |
| 157 | + - org/repo#123 |
| 158 | + """ |
| 159 | + # Match #123 format |
| 160 | + numbers = set() |
| 161 | + |
| 162 | + # Basic #123 format |
| 163 | + matches = re.finditer(r"(?:^|\s)#(\d+)(?:\s|$)", text) |
| 164 | + numbers.update(int(m.group(1)) for m in matches) |
| 165 | + |
| 166 | + # org/repo#123 or repo#123 format (only care about numbers in current repo) |
| 167 | + matches = re.finditer(r"(?:[\w-]+/)?[\w-]+#(\d+)", text) |
| 168 | + numbers.update(int(m.group(1)) for m in matches) |
| 169 | + |
| 170 | + return sorted(numbers) |
| 171 | + |
| 172 | + |
| 173 | +def fetch_linked_items(gh_client: Github, numbers: List[int]) -> List[LinkedItem]: |
| 174 | + """Fetch full context of linked issues/PRs""" |
| 175 | + repo = gh_client.get_repo(os.getenv("GITHUB_REPOSITORY")) |
| 176 | + linked_items = [] |
| 177 | + |
| 178 | + for number in numbers: |
| 179 | + try: |
| 180 | + issue = repo.get_issue(number) |
| 181 | + item_type = "pull_request" if issue.pull_request else "issue" |
| 182 | + |
| 183 | + linked_items.append( |
| 184 | + LinkedItem( |
| 185 | + number=number, |
| 186 | + title=issue.title, |
| 187 | + body=issue.body or "", |
| 188 | + labels=[label.name for label in issue.labels], |
| 189 | + type=item_type, |
| 190 | + ) |
| 191 | + ) |
| 192 | + except Exception as e: |
| 193 | + print(f"Warning: Failed to fetch item #{number}: {e}") |
| 194 | + |
| 195 | + return linked_items |
0 commit comments