Coverage for src/workstack/status/collectors/github.py: 35%

26 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-19 09:31 -0400

1"""GitHub PR collector.""" 

2 

3from pathlib import Path 

4 

5from workstack.core.context import WorkstackContext 

6from workstack.status.collectors.base import StatusCollector 

7from workstack.status.models.status_data import PullRequestStatus 

8 

9 

10class GitHubPRCollector(StatusCollector): 

11 """Collects GitHub pull request information.""" 

12 

13 @property 

14 def name(self) -> str: 

15 """Name identifier for this collector.""" 

16 return "pr" 

17 

18 def is_available(self, ctx: WorkstackContext, worktree_path: Path) -> bool: 

19 """Check if PR information should be fetched. 

20 

21 Args: 

22 ctx: Workstack context 

23 worktree_path: Path to worktree 

24 

25 Returns: 

26 True if PR info is enabled in config 

27 """ 

28 if not ctx.global_config_ops.get_show_pr_info(): 

29 return False 

30 

31 if not worktree_path.exists(): 

32 return False 

33 

34 return True 

35 

36 def collect( 

37 self, ctx: WorkstackContext, worktree_path: Path, repo_root: Path 

38 ) -> PullRequestStatus | None: 

39 """Collect GitHub PR information. 

40 

41 Args: 

42 ctx: Workstack context 

43 worktree_path: Path to worktree 

44 repo_root: Repository root path 

45 

46 Returns: 

47 PullRequestStatus with PR information or None if collection fails 

48 """ 

49 branch = ctx.git_ops.get_current_branch(worktree_path) 

50 if branch is None: 

51 return None 

52 

53 # Try Graphite first (fast - no CI status) 

54 prs = ctx.graphite_ops.get_prs_from_graphite(ctx.git_ops, repo_root) 

55 

56 # If Graphite data not available, fall back to GitHub 

57 if not prs: 

58 prs = ctx.github_ops.get_prs_for_repo(repo_root, include_checks=True) 

59 

60 # Find PR for current branch 

61 pr = prs.get(branch) 

62 if pr is None: 

63 return None 

64 

65 # Determine if ready to merge 

66 ready_to_merge = ( 

67 pr.state == "OPEN" 

68 and not pr.is_draft 

69 and (pr.checks_passing is True or pr.checks_passing is None) 

70 ) 

71 

72 return PullRequestStatus( 

73 number=pr.number, 

74 title=None, # Title not available in PullRequestInfo 

75 state=pr.state, 

76 is_draft=pr.is_draft, 

77 url=pr.url, 

78 checks_passing=pr.checks_passing, 

79 reviews=None, # Reviews not available in PullRequestInfo 

80 ready_to_merge=ready_to_merge, 

81 )