feat(github): implement PRReader + FileReader client (#80) #93
@@ -199,7 +199,7 @@ func (c *Client) doRequest(ctx context.Context, method, reqURL string, accept st
|
||||
timer := time.NewTimer(delay)
|
||||
|
|
||||
select {
|
||||
case <-timer.C:
|
||||
// Timer already fired; Stop() is a no-op here.
|
||||
// Backoff elapsed, proceed with retry.
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return nil, ctx.Err()
|
||||
@@ -259,8 +259,8 @@ func (c *Client) doRequest(ctx context.Context, method, reqURL string, accept st
|
||||
if attempt < len(backoff) {
|
||||
|
sonnet-review-bot
commented
[MINOR] The **[MINOR]** The `int64` cast in `if int64(len(body)) >= maxResponseBytes` is unnecessary since `maxResponseBytes` is an untyped constant and `len()` returns `int`. Both sides of the comparison are `int`-typed. The cast is harmless but adds noise.
|
||||
backoff[attempt] = delay
|
||||
}
|
||||
} else if t, err := http.ParseTime(ra); err == nil {
|
||||
delay := time.Until(t)
|
||||
} else if retryAt, err := http.ParseTime(ra); err == nil {
|
||||
delay := time.Until(retryAt)
|
||||
|
sonnet-review-bot
commented
[NIT] In **[NIT]** In `doRequest`, after the `handleResponse` call, there's a check `if resp.StatusCode == http.StatusTooManyRequests` but `resp` could theoretically have been closed by `handleResponse`. The body is closed, but the `resp.StatusCode` field is still accessible on the struct. This is correct and safe in Go's net/http — just worth being aware of.
|
||||
if delay < 0 {
|
||||
delay = 0
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ func (c *Client) ListContents(ctx context.Context, owner, repo, path string) ([]
|
||||
if err2 := json.Unmarshal(body, &single); err2 != nil {
|
||||
return nil, fmt.Errorf("parse contents JSON: %w", err2)
|
||||
}
|
||||
|
security-review-bot marked this conversation as resolved
Outdated
[MINOR] escapePath preserves dot-segments ("." and "..") in path components. Some servers normalize dot-segments, which could let an untrusted path escape the intended "contents" endpoint (e.g., "/contents/../../pulls"). While this stays on the same host, it could lead to unexpected behavior. Consider rejecting or encoding dot-segments to prevent path traversal in URL construction. **[MINOR]** escapePath preserves dot-segments ("." and "..") in path components. Some servers normalize dot-segments, which could let an untrusted path escape the intended "contents" endpoint (e.g., "/contents/../../pulls"). While this stays on the same host, it could lead to unexpected behavior. Consider rejecting or encoding dot-segments to prevent path traversal in URL construction.
|
||||
// Guard against empty objects ({}) or unexpected shapes that
|
||||
|
sonnet-review-bot
commented
[MINOR] Silent empty-array handling: when the GitHub Contents API returns an empty JSON array **[MINOR]** Silent empty-array handling: when the GitHub Contents API returns an empty JSON array `[]`, `json.Unmarshal` into `[]entry` succeeds with `len(entries) == 0` and the function returns an empty slice without error. This is probably the correct behavior for an empty directory, but it's undocumented and could silently mask an unexpected server response. A comment clarifying this intent would be valuable.
|
||||
// unmarshal successfully but carry no useful data.
|
||||
if single.Name == "" && single.Path == "" && single.Type == "" {
|
||||
return nil, fmt.Errorf("parse contents JSON: unexpected response format")
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ func (c *Client) GetCommitStatuses(ctx context.Context, owner, repo, sha string)
|
||||
for _, cr := range checkResp.CheckRuns {
|
||||
result = append(result, vcs.CommitStatus{
|
||||
|
sonnet-review-bot
commented
[MINOR] The **[MINOR]** The `mapCheckRunStatus` mapping treats `cancelled`, `skipped`, and `neutral` as `"success"` (non-blocking). This is a policy decision that should be documented more prominently — `cancelled` in particular could reasonably be mapped to `failure` in some contexts. The comment `// non-blocking` is brief; a note explaining the design rationale (e.g. 'these do not indicate a blocking failure per GitHub's check suite semantics') would help future maintainers.
|
||||
Context: cr.Name,
|
||||
|
sonnet-review-bot
commented
[NIT] The **[NIT]** The `mapCheckRunStatus` function signature takes an ignored second parameter `_ string`. The comment explains why, which is good. A minor alternative would be to not accept the parameter at all and have the caller omit it, but given this is a private helper called from one place with both values available, the current approach is defensible. The doc comment adequately explains the design choice.
sonnet-review-bot
commented
[MINOR] The **[MINOR]** The `mapCheckRunStatus` function signature `func mapCheckRunStatus(conclusion *string, _ string) string` uses a blank identifier for the `status` parameter. While the comment explains why, a cleaner approach given the parameter is truly unused would be to just not include it and update the call site — or if the signature must stay for extensibility, at minimum name it (e.g., `checkStatus string`) so readers can understand what's being ignored. The blank `_` with no name makes the call site `mapCheckRunStatus(cr.Conclusion, cr.Status)` harder to understand without reading the comment.
|
||||
Status: mapCheckRunStatus(cr.Conclusion, cr.Status),
|
||||
Status: mapCheckRunStatus(cr.Conclusion),
|
||||
|
sonnet-review-bot
commented
[MINOR] mapCheckRunStatus ignores the second parameter (status string) entirely — it's named **[MINOR]** mapCheckRunStatus ignores the second parameter (status string) entirely — it's named `_`. The function signature accepts the status for potential future use, but the unused parameter with a blank name could be confusing. If the `status` field (e.g. 'in_progress', 'queued') is never needed, the parameter should be removed from the signature; if it may be needed, a comment explaining why it's currently unused would help. The current behavior maps nil conclusion to 'pending' regardless of status, which may be correct, but 'stale' and 'waiting' conclusions (which GitHub also uses) would silently fall through to 'pending' rather than being explicit.
|
||||
Description: derefString(cr.Conclusion),
|
||||
TargetURL: cr.HTMLURL,
|
||||
})
|
||||
@@ -208,10 +208,9 @@ func (c *Client) GetCommitStatuses(ctx context.Context, owner, repo, sha string)
|
||||
}
|
||||
|
||||
// mapCheckRunStatus maps a check run conclusion to a vcs.CommitStatus status string.
|
||||
|
sonnet-review-bot
commented
[MINOR] In **[MINOR]** In `mapCheckRunStatus`, the cases `"in_progress"` and `"queued"` are in the `switch *conclusion` block, but `conclusion` is non-nil when this switch is reached. GitHub's API sets `conclusion` to null for in-progress/queued check runs, not to the strings `"in_progress"` or `"queued"` — those are `status` field values. So these cases are dead code and will never be reached. The nil-conclusion check at the top already handles the pending case correctly.
|
||||
// The second parameter (check run status field, e.g. "completed", "in_progress") is
|
||||
// unused because conclusion alone determines the mapped state: nil conclusion means
|
||||
// the run is still in progress (pending), regardless of the status field value.
|
||||
func mapCheckRunStatus(conclusion *string, _ string) string {
|
||||
// Conclusion alone determines the mapped state: nil conclusion means the run is
|
||||
// still in progress (pending), regardless of the status field value.
|
||||
func mapCheckRunStatus(conclusion *string) string {
|
||||
if conclusion == nil {
|
||||
// Still running or queued
|
||||
return "pending"
|
||||
|
||||
[NIT] The
doRequestmethod hasaccept stringas the last parameter. Per Go idiom (style.md receiver naming / api-conventions), named parameters for Accept headers are fine, but the parameter nameacceptconflicts with the common Go pattern of usingacceptonly in request context. Not a correctness issue — purely style.