Skip to content

Commit 68934e8

Browse files
committed
Now it kinda works
1 parent 987fe0a commit 68934e8

22 files changed

+2220
-4875
lines changed

analysis.go

Lines changed: 0 additions & 590 deletions
This file was deleted.

better-reviewers

7.74 MB
Binary file not shown.

better-reviewers-new

7.76 MB
Binary file not shown.

cache.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package main
2+
3+
import (
4+
"sync"
5+
"time"
6+
)
7+
8+
// cacheEntry holds a cached value with expiration.
9+
type cacheEntry struct {
10+
value interface{}
11+
expiration time.Time
12+
}
13+
14+
// cache provides thread-safe caching with TTL.
15+
type cache struct {
16+
mu sync.RWMutex
17+
entries map[string]cacheEntry
18+
ttl time.Duration
19+
}
20+
21+
// newCache creates a new cache with the given TTL.
22+
func newCache(ttl time.Duration) *cache {
23+
return &cache{
24+
entries: make(map[string]cacheEntry),
25+
ttl: ttl,
26+
}
27+
}
28+
29+
// get retrieves a value from cache if not expired.
30+
func (c *cache) get(key string) (interface{}, bool) {
31+
c.mu.RLock()
32+
defer c.mu.RUnlock()
33+
34+
entry, exists := c.entries[key]
35+
if !exists || time.Now().After(entry.expiration) {
36+
return nil, false
37+
}
38+
39+
return entry.value, true
40+
}
41+
42+
// set stores a value in cache with TTL.
43+
func (c *cache) set(key string, value interface{}) {
44+
c.mu.Lock()
45+
defer c.mu.Unlock()
46+
47+
c.entries[key] = cacheEntry{
48+
value: value,
49+
expiration: time.Now().Add(c.ttl),
50+
}
51+
}
52+
53+
// clear removes all entries from cache.
54+
func (c *cache) clear() {
55+
c.mu.Lock()
56+
defer c.mu.Unlock()
57+
58+
c.entries = make(map[string]cacheEntry)
59+
}

constants.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import "time"
4+
5+
// Selection methods for reviewer choice tracking
6+
const (
7+
selectionAssignee = "assignee-expert"
8+
selectionAuthorOverlap = "author-overlap"
9+
selectionAuthorDirectory = "author-directory"
10+
selectionAuthorProject = "author-project"
11+
selectionReviewerCommenter = "reviewer-commenter"
12+
selectionReviewerOverlap = "reviewer-overlap"
13+
selectionReviewerDirectory = "reviewer-directory"
14+
selectionReviewerProject = "reviewer-project"
15+
)
16+
17+
// Configuration constants
18+
const (
19+
httpTimeout = 120 // seconds
20+
maxRetries = 3
21+
retryDelay = 2 // seconds
22+
nearbyLines = 3 // lines within this distance count as "nearby"
23+
maxFilesToAnalyze = 10
24+
maxHistoricalPRs = 50 // With caching, we can afford more lookups
25+
maxRecentPRs = 20 // Reasonable limit for recent PRs
26+
cacheTTL = 15 * time.Minute // Cache results for 15 minutes
27+
)

finders.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package main
2+
3+
import (
4+
"context"
5+
)
6+
7+
// findDirectoryAuthor finds the most recent author in the affected directories.
8+
func (rf *ReviewerFinder) findDirectoryAuthor(ctx context.Context, pr *PullRequest, files []string) string {
9+
directories := rf.getDirectories(files)
10+
11+
for _, dir := range directories {
12+
if author := rf.findRecentAuthorInDirectory(ctx, pr.Owner, pr.Repository, dir); author != "" {
13+
if author != pr.Author && rf.isValidReviewer(ctx, pr, author) {
14+
return author
15+
}
16+
}
17+
}
18+
19+
return ""
20+
}
21+
22+
// findProjectAuthor finds the most recent author in the project.
23+
func (rf *ReviewerFinder) findProjectAuthor(ctx context.Context, pr *PullRequest) string {
24+
authors := rf.findRecentAuthorsInProject(ctx, pr.Owner, pr.Repository, 10)
25+
26+
for _, author := range authors {
27+
if author != pr.Author && rf.isValidReviewer(ctx, pr, author) {
28+
return author
29+
}
30+
}
31+
32+
return ""
33+
}
34+
35+
// findCommenterReviewer finds recent PR commenters who can review.
36+
func (rf *ReviewerFinder) findCommenterReviewer(ctx context.Context, pr *PullRequest, excludeAuthor string) string {
37+
excludeUsers := []string{pr.Author}
38+
if excludeAuthor != "" {
39+
excludeUsers = append(excludeUsers, excludeAuthor)
40+
}
41+
42+
commenters, err := rf.getRecentPRCommenters(ctx, pr.Owner, pr.Repository, excludeUsers)
43+
if err != nil {
44+
return ""
45+
}
46+
47+
for _, commenter := range commenters {
48+
if rf.isValidReviewer(ctx, pr, commenter) {
49+
return commenter
50+
}
51+
}
52+
53+
return ""
54+
}
55+
56+
// findDirectoryReviewer finds the most active reviewer in the affected directories.
57+
func (rf *ReviewerFinder) findDirectoryReviewer(ctx context.Context, pr *PullRequest, files []string, excludeAuthor string) string {
58+
directories := rf.getDirectories(files)
59+
60+
for _, dir := range directories {
61+
if reviewer := rf.findActiveReviewerInDirectory(ctx, pr.Owner, pr.Repository, dir); reviewer != "" {
62+
if reviewer != pr.Author && reviewer != excludeAuthor && rf.isValidReviewer(ctx, pr, reviewer) {
63+
return reviewer
64+
}
65+
}
66+
}
67+
68+
return ""
69+
}
70+
71+
// findProjectReviewer finds the most active reviewer in the project.
72+
func (rf *ReviewerFinder) findProjectReviewer(ctx context.Context, pr *PullRequest, excludeAuthor string) string {
73+
reviewers := rf.findActiveReviewersInProject(ctx, pr.Owner, pr.Repository, 10)
74+
75+
for _, reviewer := range reviewers {
76+
if reviewer != pr.Author && reviewer != excludeAuthor && rf.isValidReviewer(ctx, pr, reviewer) {
77+
return reviewer
78+
}
79+
}
80+
81+
return ""
82+
}
83+
84+
// findRecentAuthorInDirectory finds the most recent commit author in a directory.
85+
func (rf *ReviewerFinder) findRecentAuthorInDirectory(ctx context.Context, owner, repo, directory string) string {
86+
prs, err := rf.getRecentPRsInDirectory(ctx, owner, repo, directory)
87+
if err != nil {
88+
return ""
89+
}
90+
91+
fc := make(frequencyCounter)
92+
for _, pr := range prs {
93+
fc.add(pr.Author)
94+
}
95+
return fc.best()
96+
}
97+
98+
// findActiveReviewerInDirectory finds the most active reviewer in a directory.
99+
func (rf *ReviewerFinder) findActiveReviewerInDirectory(ctx context.Context, owner, repo, directory string) string {
100+
prs, err := rf.getRecentPRsInDirectory(ctx, owner, repo, directory)
101+
if err != nil {
102+
return ""
103+
}
104+
105+
fc := make(frequencyCounter)
106+
for _, pr := range prs {
107+
fc.addAll(pr.Reviewers)
108+
}
109+
return fc.best()
110+
}
111+
112+
// findRecentAuthorsInProject finds recent commit authors in the project.
113+
func (rf *ReviewerFinder) findRecentAuthorsInProject(ctx context.Context, owner, repo string, limit int) []string {
114+
prs, err := rf.getRecentPRsInProject(ctx, owner, repo)
115+
if err != nil {
116+
return nil
117+
}
118+
119+
fc := make(frequencyCounter)
120+
for _, pr := range prs {
121+
fc.add(pr.Author)
122+
}
123+
return fc.top(limit)
124+
}
125+
126+
// findActiveReviewersInProject finds active reviewers in the project.
127+
func (rf *ReviewerFinder) findActiveReviewersInProject(ctx context.Context, owner, repo string, limit int) []string {
128+
prs, err := rf.getRecentPRsInProject(ctx, owner, repo)
129+
if err != nil {
130+
return nil
131+
}
132+
133+
fc := make(frequencyCounter)
134+
for _, pr := range prs {
135+
fc.addAll(pr.Reviewers)
136+
}
137+
return fc.top(limit)
138+
}
139+

0 commit comments

Comments
 (0)