Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 2h ago 4b8c9e1d
history blame raw 4.3 KiB
1package index
2
3// A repository's contributors, counted from the work the forge records rather
4// than from the author text in commits.
5//
6// A commit author is whatever the pusher's git config said; an account is a name
7// the forge authenticated. There is no field that joins the two, so the overview
8// ranks accounts by what the metadata branch proves they did: changes that
9// merged, the issues those changes fixed, the issues they reported that were
10// fixed, and the reviews that named the head that merged.
11//
12// Every count is for one repository. Whether a viewer may see that repository is
13// the caller's question; the overview has already opened it before it asks.
14
15// ContributorSummary is one account's counted work in one repository.
16type ContributorSummary struct {
17 // Merged is the number of change requests the account authored that reached
18 // the merged state.
19 Merged int
20 // Fixed is the number of distinct issues closed by those merged changes.
21 Fixed int
22 // Reported is the number of distinct issues the account opened that a merged
23 // change closed. An account that reports and fixes the same issue is counted
24 // in both: diagnosing and fixing are two contributions.
25 Reported int
26 // Reviews is the number of merged changes whose approval names the head and
27 // base that merged. An approval invalidated by a later push is not a review
28 // that gated a landing, so it is not counted.
29 Reviews int
30}
31
32// Total is the plain sum of the four counts. It is used only to order the
33// overview's short list; it is not a score with hidden weights.
34func (c ContributorSummary) Total() int {
35 return c.Merged + c.Fixed + c.Reported + c.Reviews
36}
37
38// ContributorSummaries returns one row per account with counted work in the
39// repository, keyed by account name. An account with nothing counted is absent
40// rather than present with four zeroes.
41func (s *Store) ContributorSummaries(repoID string) (map[string]ContributorSummary, error) {
42 out := map[string]ContributorSummary{}
43 if repoID == "" {
44 return out, nil
45 }
46 queries := []struct {
47 sql string
48 add func(*ContributorSummary, int)
49 }{
50 {
51 // Changes that landed, credited to the account that wrote them.
52 sql: `SELECT author, COUNT(*) FROM requests
53 WHERE repo_id=? AND state='merged'
54 GROUP BY author`,
55 add: func(c *ContributorSummary, n int) { c.Merged = n },
56 },
57 {
58 // Issues those changes closed. The issue must still be closed: a
59 // reopened issue is not a fix that stands.
60 sql: `SELECT r.author, COUNT(DISTINCT rc.issue_id)
61 FROM requests r
62 JOIN request_closes rc ON rc.repo_id=r.repo_id AND rc.request_id=r.id
63 JOIN issues i ON i.repo_id=rc.repo_id AND i.id=rc.issue_id
64 WHERE r.repo_id=? AND r.state='merged' AND i.state='closed'
65 GROUP BY r.author`,
66 add: func(c *ContributorSummary, n int) { c.Fixed = n },
67 },
68 {
69 // Issues the account opened that a merged change closed. A manual
70 // close without a landing is deliberately not counted: it does not
71 // prove the report led to a fix.
72 sql: `SELECT i.author, COUNT(DISTINCT i.id)
73 FROM issues i
74 JOIN request_closes rc ON rc.repo_id=i.repo_id AND rc.issue_id=i.id
75 JOIN requests r ON r.repo_id=rc.repo_id AND r.id=rc.request_id
76 WHERE i.repo_id=? AND i.state='closed' AND r.state='merged'
77 GROUP BY i.author`,
78 add: func(c *ContributorSummary, n int) { c.Reported = n },
79 },
80 {
81 // Approvals that still name the merged head and base, which is the
82 // same rule the landing gate applies to a current approval.
83 sql: `SELECT a.principal, COUNT(DISTINCT a.request_id)
84 FROM request_approvals a
85 JOIN requests r ON r.repo_id=a.repo_id AND r.id=a.request_id
86 WHERE a.repo_id=? AND r.state='merged'
87 AND a.head=r.head AND a.base=r.base
88 GROUP BY a.principal`,
89 add: func(c *ContributorSummary, n int) { c.Reviews = n },
90 },
91 }
92 for _, q := range queries {
93 rows, err := s.db.Query(q.sql, repoID)
94 if err != nil {
95 return nil, err
96 }
97 for rows.Next() {
98 var (
99 name string
100 n int
101 )
102 if err := rows.Scan(&name, &n); err != nil {
103 rows.Close()
104 return nil, err
105 }
106 sum := out[name]
107 q.add(&sum, n)
108 out[name] = sum
109 }
110 if err := rows.Err(); err != nil {
111 rows.Close()
112 return nil, err
113 }
114 rows.Close()
115 }
116 return out, nil
117}
118