Skip to content

Commit 2626277

Browse files
committed
lint fixes
1 parent a913d00 commit 2626277

12 files changed

+824
-495
lines changed

batch_processor.go

Lines changed: 157 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -255,34 +255,73 @@ func (rf *ReviewerFinder) prsForOrgWithBatchSize(ctx context.Context, org string
255255

256256
// parseOrgPRsFromGraphQL parses PRs from GraphQL response.
257257
func (rf *ReviewerFinder) parseOrgPRsFromGraphQL(result map[string]any) (prs []*PullRequest, hasNextPage bool, cursor string) {
258+
repos := rf.extractOrgRepositories(result)
259+
if repos == nil {
260+
return prs, false, ""
261+
}
258262

259-
// Navigate through the GraphQL response structure
260-
if data, ok := result["data"].(map[string]any); ok {
261-
if org, ok := data["organization"].(map[string]any); ok {
262-
if repos, ok := org["repositories"].(map[string]any); ok {
263-
// Get pagination info
264-
if pageInfo, ok := repos["pageInfo"].(map[string]any); ok {
265-
if next, ok := pageInfo["hasNextPage"].(bool); ok {
266-
hasNextPage = next
267-
}
268-
if endCursor, ok := pageInfo["endCursor"].(string); ok {
269-
cursor = endCursor
270-
}
271-
}
263+
hasNextPage, cursor = rf.extractPaginationInfo(repos)
264+
prs = rf.extractPRsFromRepos(repos)
272265

273-
// Process repositories
274-
if nodes, ok := repos["nodes"].([]any); ok {
275-
for _, node := range nodes {
276-
if repo, ok := node.(map[string]any); ok {
277-
prs = append(prs, rf.parsePRsFromRepo(repo)...)
278-
}
279-
}
280-
}
281-
}
266+
return prs, hasNextPage, cursor
267+
}
268+
269+
// extractOrgRepositories extracts the repositories object from org GraphQL response.
270+
func (*ReviewerFinder) extractOrgRepositories(result map[string]any) map[string]any {
271+
data, ok := result["data"].(map[string]any)
272+
if !ok {
273+
return nil
274+
}
275+
276+
org, ok := data["organization"].(map[string]any)
277+
if !ok {
278+
return nil
279+
}
280+
281+
repos, ok := org["repositories"].(map[string]any)
282+
if !ok {
283+
return nil
284+
}
285+
286+
return repos
287+
}
288+
289+
// extractPaginationInfo extracts pagination info from repositories response.
290+
func (*ReviewerFinder) extractPaginationInfo(repos map[string]any) (hasNextPage bool, cursor string) {
291+
pageInfo, ok := repos["pageInfo"].(map[string]any)
292+
if !ok {
293+
return false, ""
294+
}
295+
296+
if next, ok := pageInfo["hasNextPage"].(bool); ok {
297+
hasNextPage = next
298+
}
299+
300+
if endCursor, ok := pageInfo["endCursor"].(string); ok {
301+
cursor = endCursor
302+
}
303+
304+
return hasNextPage, cursor
305+
}
306+
307+
// extractPRsFromRepos extracts PRs from repository nodes.
308+
func (rf *ReviewerFinder) extractPRsFromRepos(repos map[string]any) []*PullRequest {
309+
var prs []*PullRequest
310+
311+
nodes, ok := repos["nodes"].([]any)
312+
if !ok {
313+
return prs
314+
}
315+
316+
for _, node := range nodes {
317+
repo, ok := node.(map[string]any)
318+
if !ok {
319+
continue
282320
}
321+
prs = append(prs, rf.parsePRsFromRepo(repo)...)
283322
}
284323

285-
return prs, hasNextPage, cursor
324+
return prs
286325
}
287326

288327
// parsePRsFromRepo parses PRs from a repository node.
@@ -323,26 +362,43 @@ func (rf *ReviewerFinder) parsePRFromGraphQL(prData map[string]any, owner, repo
323362
Repository: repo,
324363
}
325364

326-
// Parse basic fields
365+
rf.parseBasicPRFields(prData, pr)
366+
rf.parsePRAuthor(prData, pr)
367+
rf.parsePRDates(prData, pr)
368+
rf.parsePRAssignees(prData, pr)
369+
rf.parsePRReviewers(prData, pr)
370+
rf.parsePRLastCommit(prData, pr)
371+
372+
return pr
373+
}
374+
375+
// parseBasicPRFields parses basic PR fields like number, title, and draft status.
376+
func (*ReviewerFinder) parseBasicPRFields(prData map[string]any, pr *PullRequest) {
327377
if number, ok := prData["number"].(float64); ok {
328378
pr.Number = int(number)
329379
}
330380
if title, ok := prData["title"].(string); ok {
331381
pr.Title = title
332382
}
333-
// URL field doesn't exist in PullRequest struct, skip it
334383
if isDraft, ok := prData["isDraft"].(bool); ok {
335384
pr.Draft = isDraft
336385
}
386+
}
337387

338-
// Parse author
339-
if author, ok := prData["author"].(map[string]any); ok {
340-
if login, ok := author["login"].(string); ok {
341-
pr.Author = login
342-
}
388+
// parsePRAuthor parses the PR author information.
389+
func (*ReviewerFinder) parsePRAuthor(prData map[string]any, pr *PullRequest) {
390+
author, ok := prData["author"].(map[string]any)
391+
if !ok {
392+
return
343393
}
344394

345-
// Parse dates
395+
if login, ok := author["login"].(string); ok {
396+
pr.Author = login
397+
}
398+
}
399+
400+
// parsePRDates parses PR creation and update dates.
401+
func (*ReviewerFinder) parsePRDates(prData map[string]any, pr *PullRequest) {
346402
if createdAt, ok := prData["createdAt"].(string); ok {
347403
if t, err := time.Parse(time.RFC3339, createdAt); err == nil {
348404
pr.CreatedAt = t
@@ -353,60 +409,86 @@ func (rf *ReviewerFinder) parsePRFromGraphQL(prData map[string]any, owner, repo
353409
pr.UpdatedAt = t
354410
}
355411
}
412+
}
356413

357-
// Parse assignees
358-
if assignees, ok := prData["assignees"].(map[string]any); ok {
359-
if nodes, ok := assignees["nodes"].([]any); ok {
360-
for _, node := range nodes {
361-
if assignee, ok := node.(map[string]any); ok {
362-
if login, ok := assignee["login"].(string); ok {
363-
pr.Assignees = append(pr.Assignees, login)
364-
}
365-
}
366-
}
367-
}
414+
// parsePRAssignees parses the list of PR assignees.
415+
func (*ReviewerFinder) parsePRAssignees(prData map[string]any, pr *PullRequest) {
416+
assignees, ok := prData["assignees"].(map[string]any)
417+
if !ok {
418+
return
368419
}
369420

370-
// Parse existing reviewers
371-
if reviewRequests, ok := prData["reviewRequests"].(map[string]any); ok {
372-
if nodes, ok := reviewRequests["nodes"].([]any); ok {
373-
for _, node := range nodes {
374-
if request, ok := node.(map[string]any); ok {
375-
if reviewer, ok := request["requestedReviewer"].(map[string]any); ok {
376-
if login, ok := reviewer["login"].(string); ok {
377-
pr.Reviewers = append(pr.Reviewers, login)
378-
}
379-
}
380-
}
381-
}
421+
nodes, ok := assignees["nodes"].([]any)
422+
if !ok {
423+
return
424+
}
425+
426+
for _, node := range nodes {
427+
assignee, ok := node.(map[string]any)
428+
if !ok {
429+
continue
430+
}
431+
432+
if login, ok := assignee["login"].(string); ok {
433+
pr.Assignees = append(pr.Assignees, login)
382434
}
383435
}
436+
}
384437

385-
// Parse last commit date
386-
if commits, ok := prData["commits"].(map[string]any); ok {
387-
if nodes, ok := commits["nodes"].([]any); ok && len(nodes) > 0 {
388-
if commit, ok := nodes[0].(map[string]any); ok {
389-
if commitData, ok := commit["commit"].(map[string]any); ok {
390-
if committedDate, ok := commitData["committedDate"].(string); ok {
391-
if t, err := time.Parse(time.RFC3339, committedDate); err == nil {
392-
pr.LastCommit = t
393-
}
394-
}
395-
}
396-
}
438+
// parsePRReviewers parses the list of requested reviewers.
439+
func (*ReviewerFinder) parsePRReviewers(prData map[string]any, pr *PullRequest) {
440+
reviewRequests, ok := prData["reviewRequests"].(map[string]any)
441+
if !ok {
442+
return
443+
}
444+
445+
nodes, ok := reviewRequests["nodes"].([]any)
446+
if !ok {
447+
return
448+
}
449+
450+
for _, node := range nodes {
451+
request, ok := node.(map[string]any)
452+
if !ok {
453+
continue
454+
}
455+
456+
reviewer, ok := request["requestedReviewer"].(map[string]any)
457+
if !ok {
458+
continue
459+
}
460+
461+
if login, ok := reviewer["login"].(string); ok {
462+
pr.Reviewers = append(pr.Reviewers, login)
397463
}
398464
}
465+
}
399466

400-
// LastReview will be fetched on-demand when needed for detailed analysis
401-
// Removed from initial query to reduce GraphQL payload size
467+
// parsePRLastCommit parses the last commit date from the PR.
468+
func (*ReviewerFinder) parsePRLastCommit(prData map[string]any, pr *PullRequest) {
469+
commits, ok := prData["commits"].(map[string]any)
470+
if !ok {
471+
return
472+
}
473+
474+
nodes, ok := commits["nodes"].([]any)
475+
if !ok || len(nodes) == 0 {
476+
return
477+
}
402478

403-
// Parse file statistics - these fields don't exist in our PullRequest struct
404-
// We would need to fetch file details separately if needed
405-
// additions, deletions, and changedFiles are available in the GraphQL response
406-
// but our PullRequest struct doesn't have these fields directly
479+
commit, ok := nodes[0].(map[string]any)
480+
if !ok {
481+
return
482+
}
407483

408-
// Note: ChangedFiles array would need a separate query for file details
409-
// For now, we'll fetch those on-demand when needed
484+
commitData, ok := commit["commit"].(map[string]any)
485+
if !ok {
486+
return
487+
}
410488

411-
return pr
489+
if committedDate, ok := commitData["committedDate"].(string); ok {
490+
if t, err := time.Parse(time.RFC3339, committedDate); err == nil {
491+
pr.LastCommit = t
492+
}
493+
}
412494
}

better-reviewers

364 KB
Binary file not shown.

constants.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ const (
3636
prCountCacheTTL = 6 * time.Hour // PR count for workload balancing (default).
3737
prCountFailureCacheTTL = 10 * time.Minute // Cache failures to avoid repeated API calls.
3838
prStaleDaysThreshold = 90 // PRs older than this are considered stale.
39+
maxTokenLength = 100 // Maximum expected length for GitHub tokens.
40+
maxURLLength = 500 // Maximum URL length to validate.
41+
maxPRNumber = 999999 // Maximum PR number to validate.
42+
maxGitHubNameLength = 100 // Maximum length for GitHub owner/repo names.
3943

4044
// API and pagination limits.
4145
perPageLimit = 100 // GitHub API per_page limit
@@ -94,12 +98,12 @@ const (
9498
reviewerWeightMultiplier = 0.5 // Weight multiplier for reviewers vs authors
9599

96100
// Overlap scoring constants.
97-
contextMatchWeight = 0.7 // Weight for context matches in overlap scoring
101+
contextMatchWeight = 0.7 // Weight for context matches in overlap scoring
98102
minOverlapThreshold = 5.0 // Minimum overlap score threshold
99103

100104
// Analysis limits.
101-
maxRecentCommits = 10 // Maximum recent commits to analyze
102-
maxDirectoryReviewers = 5 // Maximum directory reviewers to return
105+
maxRecentCommits = 10 // Maximum recent commits to analyze
106+
maxDirectoryReviewers = 5 // Maximum directory reviewers to return
103107

104108
// Batch processing sizes.
105109
defaultBatchSize = 20 // Default batch size for processing

github_client.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,23 @@ func newGitHubClient(ctx context.Context, useAppAuth bool) (*GitHubClient, error
8484
isAppAuth = true
8585
log.Print("[AUTH] Using GitHub App authentication")
8686
} else {
87-
// Use gh CLI authentication
87+
// Use gh CLI authentication with validation
8888
cmd := exec.CommandContext(ctx, "gh", "auth", "token")
8989
output, err := cmd.Output()
9090
if err != nil {
9191
return nil, fmt.Errorf("failed to get GitHub token: %w", err)
9292
}
9393

9494
token = strings.TrimSpace(string(output))
95+
// Validate token format (should be alphanumeric with underscores)
96+
if token == "" || len(token) > maxTokenLength {
97+
return nil, errors.New("invalid token length from gh command")
98+
}
99+
for _, r := range token {
100+
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && r != '_' {
101+
return nil, errors.New("invalid token format from gh command")
102+
}
103+
}
95104
if token == "" {
96105
return nil, errors.New("no GitHub token found")
97106
}
@@ -116,7 +125,12 @@ func newGitHubClient(ctx context.Context, useAppAuth bool) (*GitHubClient, error
116125

117126
// makeRequest makes an HTTP request to the GitHub API with retry logic.
118127
func (c *GitHubClient) makeRequest(ctx context.Context, method, apiURL string, body any) (*http.Response, error) {
119-
log.Printf("[HTTP] %s %s", method, apiURL)
128+
// Sanitize URL for logging - remove potential sensitive query parameters
129+
sanitizedURL := apiURL
130+
if idx := strings.Index(apiURL, "?"); idx != -1 {
131+
sanitizedURL = apiURL[:idx] + "?[REDACTED]"
132+
}
133+
log.Printf("[HTTP] %s %s", method, sanitizedURL)
120134

121135
var resp *http.Response
122136
err := retryWithBackoff(ctx, fmt.Sprintf("%s %s", method, apiURL), func() error {
@@ -175,7 +189,13 @@ func (c *GitHubClient) makeRequest(ctx context.Context, method, apiURL string, b
175189
return nil, err
176190
}
177191

178-
log.Printf("[HTTP] %s %s - Status: %d", method, apiURL, resp.StatusCode)
192+
// Sanitize URL for logging (reuse variable)
193+
if idx := strings.Index(apiURL, "?"); idx != -1 {
194+
sanitizedURL = apiURL[:idx] + "?[REDACTED]"
195+
} else {
196+
sanitizedURL = apiURL
197+
}
198+
log.Printf("[HTTP] %s %s - Status: %d", method, sanitizedURL, resp.StatusCode)
179199
return resp, nil
180200
}
181201

0 commit comments

Comments
 (0)