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 · 53m ago 4b8c9e1d
history blame raw 2.0 KiB
1package metadata
2
3// State changes are recoverable from the metadata log because SetIssueState and
4// SetRequestState write a canonical subject: the resource, its id, and the new
5// state. Reading subjects rather than diffing every issue file keeps the
6// activity feed to one git call and leaves the file format alone.
7
8import (
9 "strings"
10 "time"
11
12 "rodent.software/vole/core/git"
13 voleid "rodent.software/vole/core/id"
14)
15
16// StateChange is one closed, reopened, or merged event from the metadata branch.
17type StateChange struct {
18 Kind string // issue | request
19 ID string
20 State string // open | closed | merged
21 Actor string
22 Time time.Time
23}
24
25// LoadStateChanges reads at most n metadata commits and returns the state
26// changes among them, newest first.
27func LoadStateChanges(g *git.Repo, n int) ([]StateChange, error) {
28 commits, err := g.Log(Ref, n)
29 if err != nil {
30 return nil, err
31 }
32 out := make([]StateChange, 0, len(commits)/4)
33 for _, c := range commits {
34 kind, id, state, ok := ParseStateSubject(c.Subject)
35 if !ok {
36 continue
37 }
38 out = append(out, StateChange{
39 Kind: kind, ID: id, State: state,
40 Actor: c.Author, Time: time.Unix(c.When, 0).UTC(),
41 })
42 }
43 return out, nil
44}
45
46// ParseStateSubject reads the canonical subject SetIssueState and SetRequestState
47// write. It is exported so the index can derive the same events from the commits
48// it reads without a second copy of the format.
49func ParseStateSubject(subject string) (kind, id, state string, ok bool) {
50 fields := strings.Fields(subject)
51 if len(fields) != 3 {
52 return "", "", "", false
53 }
54 switch fields[0] {
55 case "issue:":
56 kind = "issue"
57 case "request:":
58 kind = "request"
59 default:
60 return "", "", "", false
61 }
62 id, state = fields[1], fields[2]
63 if !voleid.Valid(id) {
64 return "", "", "", false
65 }
66 switch kind {
67 case "issue":
68 if state != "open" && state != "closed" {
69 return "", "", "", false
70 }
71 case "request":
72 if state != "open" && state != "closed" && state != "merged" {
73 return "", "", "", false
74 }
75 }
76 return kind, id, state, true
77}
78