|
1 | 1 | # prx |
2 | 2 |
|
3 | | -A Go library for efficiently retrieving rich details about GitHub pull requests, including all events in chronological order. |
| 3 | +Go library for fetching pull request data from GitHub, GitLab, and Gitea/Codeberg with automatic platform detection, authentication resolution, and caching. |
4 | 4 |
|
5 | | -## Installation |
| 5 | +## Quick Start |
6 | 6 |
|
7 | | -```bash |
8 | | -go get github.com/codeGROOVE-dev/prx |
9 | | -``` |
10 | | - |
11 | | -## CLI Usage |
12 | | - |
13 | | -The repository includes a command-line tool that outputs pull request data as JSON: |
14 | | - |
15 | | -```bash |
16 | | -# Install the CLI tool |
17 | | -go install github.com/codeGROOVE-dev/prx/cmd/prx@latest |
18 | | - |
19 | | -# Authenticate with GitHub CLI (required) |
20 | | -gh auth login |
21 | | - |
22 | | -# Fetch pull request data |
23 | | -prx https://github.com/golang/go/pull/12345 |
24 | | -``` |
25 | | - |
26 | | -The CLI outputs a single JSON object containing the pull request metadata and all events: |
27 | | - |
28 | | -```bash |
29 | | -# Extract just the events |
30 | | -prx https://github.com/golang/go/pull/12345 | jq '.events[]' |
31 | | - |
32 | | -# Count events by kind |
33 | | -prx https://github.com/golang/go/pull/12345 | jq '.events[].kind' | sort | uniq -c |
34 | | - |
35 | | -# Show all review comments |
36 | | -prx https://github.com/golang/go/pull/12345 | jq '.events[] | select(.kind == "review_comment")' |
| 7 | +```go |
| 8 | +import "github.com/codeGROOVE-dev/prx/pkg/prx/fetch" |
37 | 9 |
|
38 | | -# Show PR metadata |
39 | | -prx https://github.com/golang/go/pull/12345 | jq '.pull_request' |
| 10 | +data, err := fetch.Fetch(ctx, "https://github.com/golang/go/pull/12345") |
| 11 | +// Works with: GitHub, GitLab, Codeberg, self-hosted instances |
40 | 12 | ``` |
| 13 | +Auto-detects platform and resolves authentication from `GITHUB_TOKEN`/`GITLAB_TOKEN`/`GITEA_TOKEN` or CLI tools (`gh`, `glab`, `tea`, `berg`). |
41 | 14 |
|
42 | 15 | ## Library Usage |
43 | 16 |
|
44 | 17 | ```go |
45 | | -package main |
46 | | - |
47 | 18 | import ( |
48 | | - "context" |
49 | | - "fmt" |
50 | | - "log" |
51 | | - "os" |
52 | | - |
53 | 19 | "github.com/codeGROOVE-dev/prx/pkg/prx" |
| 20 | + "github.com/codeGROOVE-dev/prx/pkg/prx/github" |
54 | 21 | ) |
55 | 22 |
|
56 | | -func main() { |
57 | | - // Create a client with your GitHub token |
58 | | - token := os.Getenv("GITHUB_TOKEN") |
59 | | - client := prx.NewClient(token) |
60 | | - |
61 | | - // Fetch pull request data |
62 | | - ctx := context.Background() |
63 | | - data, err := client.PullRequest(ctx, "owner", "repo", 123) |
64 | | - if err != nil { |
65 | | - log.Fatal(err) |
66 | | - } |
67 | | - |
68 | | - // Access PR metadata |
69 | | - fmt.Printf("PR #%d: %s\n", data.PullRequest.Number, data.PullRequest.Title) |
70 | | - fmt.Printf("Author: %s\n", data.PullRequest.Author) |
71 | | - fmt.Printf("State: %s\n", data.PullRequest.State) |
72 | | - |
73 | | - // Process events |
74 | | - for _, event := range data.Events { |
75 | | - fmt.Printf("%s: %s by %s\n", |
76 | | - event.Timestamp.Format("2006-01-02 15:04:05"), |
77 | | - event.Kind, |
78 | | - event.Actor) |
79 | | - } |
80 | | -} |
81 | | -``` |
82 | | - |
83 | | -## Data Structure |
84 | | - |
85 | | -### Pull Request Data |
86 | | - |
87 | | -The main response contains: |
88 | | - |
89 | | -```go |
90 | | -type PullRequestData struct { |
91 | | - PullRequest PullRequest `json:"pull_request"` |
92 | | - Events []Event `json:"events"` |
93 | | -} |
| 23 | +platform := github.NewPlatform(token) |
| 24 | +client := prx.NewClientWithPlatform(platform) |
| 25 | +data, err := client.PullRequest(ctx, "owner", "repo", 123) |
94 | 26 | ``` |
95 | 27 |
|
96 | | -### Pull Request Metadata |
97 | | - |
98 | | -```go |
99 | | -type PullRequest struct { |
100 | | - Number int `json:"number"` |
101 | | - Title string `json:"title"` |
102 | | - State string `json:"state"` |
103 | | - Author string `json:"author"` |
104 | | - AuthorAssociation string `json:"author_association"` |
105 | | - CreatedAt time.Time `json:"created_at"` |
106 | | - UpdatedAt time.Time `json:"updated_at"` |
107 | | - ClosedAt *time.Time `json:"closed_at,omitempty"` |
108 | | - MergedAt *time.Time `json:"merged_at,omitempty"` |
109 | | - MergeableState string `json:"mergeable_state"` |
110 | | - Additions int `json:"additions"` |
111 | | - Deletions int `json:"deletions"` |
112 | | - ChangedFiles int `json:"changed_files"` |
113 | | - Assignees []string `json:"assignees,omitempty"` |
114 | | - Reviewers map[string]ReviewState `json:"reviewers,omitempty"` |
115 | | - Labels []string `json:"labels,omitempty"` |
116 | | - TestSummary *TestSummary `json:"test_summary,omitempty"` |
117 | | - CheckSummary *CheckSummary `json:"check_summary,omitempty"` |
118 | | -} |
119 | | - |
120 | | -type TestSummary struct { |
121 | | - Passing int `json:"passing"` |
122 | | - Failing int `json:"failing"` |
123 | | - Pending int `json:"pending"` |
124 | | -} |
125 | | - |
126 | | -type CheckSummary struct { |
127 | | - Success int `json:"success"` |
128 | | - Failure int `json:"failure"` |
129 | | - Pending int `json:"pending"` |
130 | | - Neutral int `json:"neutral"` |
131 | | -} |
132 | | -``` |
133 | | - |
134 | | -### Event Structure |
135 | | - |
136 | | -Each event has a unified structure: |
137 | | - |
138 | | -```go |
139 | | -type Event struct { |
140 | | - Kind EventKind `json:"kind"` |
141 | | - Timestamp time.Time `json:"timestamp"` |
142 | | - Actor string `json:"actor"` |
143 | | - Bot bool `json:"bot,omitempty"` |
144 | | - Targets []string `json:"targets,omitempty"` |
145 | | - Outcome string `json:"outcome,omitempty"` |
146 | | - Body string `json:"body,omitempty"` |
147 | | - Question bool `json:"question,omitempty"` |
148 | | - AuthorAssociation string `json:"author_association,omitempty"` |
149 | | -} |
150 | | -``` |
151 | | - |
152 | | -### Event Examples |
153 | | - |
154 | | -```json |
155 | | -{ |
156 | | - "kind": "review", |
157 | | - "timestamp": "2024-01-15T10:30:00Z", |
158 | | - "actor": "reviewer", |
159 | | - "outcome": "approved", |
160 | | - "body": "Looks good!", |
161 | | - "author_association": "MEMBER" |
162 | | -} |
163 | | - |
164 | | -{ |
165 | | - "kind": "comment", |
166 | | - "timestamp": "2024-01-15T11:00:00Z", |
167 | | - "actor": "user123", |
168 | | - "body": "Can we add tests for this?", |
169 | | - "question": true, |
170 | | - "targets": ["@author"] |
171 | | -} |
172 | | - |
173 | | -{ |
174 | | - "kind": "labeled", |
175 | | - "timestamp": "2024-01-15T09:00:00Z", |
176 | | - "actor": "triager", |
177 | | - "targets": ["bug", "high-priority"] |
178 | | -} |
179 | | - |
180 | | -{ |
181 | | - "kind": "check_run", |
182 | | - "timestamp": "2024-01-15T12:00:00Z", |
183 | | - "actor": "github-actions", |
184 | | - "bot": true, |
185 | | - "outcome": "failure", |
186 | | - "body": "test" |
187 | | -} |
188 | | -``` |
189 | | - |
190 | | -## Event Types |
191 | | - |
192 | | -The library fetches the following event kinds: |
193 | | - |
194 | | -- **commit**: All commits in the pull request |
195 | | -- **comment**: Issue comments on the pull request |
196 | | -- **review**: Review submissions (outcome: "approved", "changes_requested", "commented") |
197 | | -- **review_comment**: Inline code review comments |
198 | | -- **status_check**: CI/CD status updates (status name in `body` field, outcome: "success", "failure", "pending", "error") |
199 | | -- **check_run**: GitHub Actions and other check runs (check name in `body` field) |
200 | | -- **assigned**, **unassigned**: Assignment changes |
201 | | -- **review_requested**, **review_request_removed**: Review request changes |
202 | | -- **labeled**, **unlabeled**: Label changes |
203 | | -- **milestoned**, **demilestoned**: Milestone changes |
204 | | -- **renamed**: Title changes |
205 | | -- **opened**, **closed**, **reopened**, **merged**: State changes |
206 | | -- **head_ref_force_pushed**: Force push to the pull request branch |
207 | | - |
208 | | -## Features |
209 | | - |
210 | | -- **Concurrent fetching** of different event types for optimal performance |
211 | | -- **Automatic pagination** handling for large pull requests |
212 | | -- **Chronological ordering** of all events |
213 | | -- **Bot detection** (marks events from bots with `"bot": true`) |
214 | | -- **Mention extraction** (populated in `targets` field for comments/reviews) |
215 | | -- **Question detection** (marks comments containing questions) |
216 | | -- **Caching support** via `prx.WithCacheStore()` for reduced API calls |
217 | | -- **Structured logging** with slog |
218 | | -- **Retry logic** with exponential backoff for API reliability |
219 | | - |
220 | | -## Caching |
221 | | - |
222 | | -Caching is enabled by default with disk persistence to the user cache directory. |
223 | | - |
224 | | -For custom cache directories: |
225 | | - |
226 | | -```go |
227 | | -store, err := prx.NewCacheStore("/tmp/prx-cache") |
228 | | -client := prx.NewClient(token, prx.WithCacheStore(store)) |
229 | | -``` |
230 | | - |
231 | | -To disable caching persistence (memory-only): |
232 | | - |
233 | | -```go |
234 | | -import "github.com/codeGROOVE-dev/fido/pkg/store/null" |
235 | | - |
236 | | -client := prx.NewClient(token, prx.WithCacheStore(null.New[string, prx.PullRequestData]())) |
237 | | -``` |
238 | | - |
239 | | -Cache entries expire after 20 days. |
240 | | - |
241 | | -## Authentication |
242 | | - |
243 | | -The library requires a GitHub personal access token or GitHub App token with: |
244 | | -- `repo` scope for private repositories |
245 | | -- `public_repo` scope for public repositories only |
246 | | - |
247 | | -## License |
| 28 | +GitLab: `gitlab.NewPlatform(token, gitlab.WithBaseURL("https://gitlab.example.com"))` | Gitea: `gitea.NewPlatform(token, gitea.WithBaseURL("https://gitea.example.com"))` |
248 | 29 |
|
249 | | -MIT |
| 30 | +Caching enabled by default. Disable: `prx.WithCacheStore(null.New[string, prx.PullRequestData]())` |
0 commit comments