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/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

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

Drop the legacy run-record import

59badc638dde0655b11d01eebfe3a457735d2501 · Keenan Nemetz · 11d ago · parent b312e166

Drop the legacy run-record import

There is no released instance to stay compatible with, so the file format
under runs/, the branch importer, and the pre-receive allowance for that
directory are gone. The Record value is now only the finish wire format and the
shape stored in run_attempts. metadata.ValidateIssues rejects runs/ like any
other unknown directory, and vole admin index rebuild no longer reads run
history from Git.

An existing instance migrates once with the previous build, then rewrites the
vole branch to remove the old records. Nothing in this build reads them.

server/hook/records_test.go −193

@@ -1,193 +0,0 @@
1 package hook
2
3 // Who may report a run result, from the gate's side: the record is the runner's, so
4 // the questions are whether the account may report at all, whether the commit is
5 // signed by that account's key, and whether the record describes the job it was handed.
6
7 import (
8 "os"
9 "os/exec"
10 "path/filepath"
11 "strings"
12 "testing"
13 "time"
14
15 "github.com/rodent-software/vole/ci/runs"
16 "github.com/rodent-software/vole/core/git"
17 "github.com/rodent-software/vole/core/id"
18 "github.com/rodent-software/vole/forge/access"
19 "github.com/rodent-software/vole/forge/identity"
20 "github.com/rodent-software/vole/forge/repo"
21 "github.com/rodent-software/vole/metadata"
22 )
23
24 // recordFixture is everything a legacy record push needs: a repository, a roster, an ACL naming
25 // one allowed runner, and a queue with a leased job to report on.
26 type recordFixture struct {
27 t *testing.T
28 dir string
29 repo *repo.Meta
30 usersDB string
31 runsDB string
32 runnerID string
33 otherID string
34 run *runs.Run
35 current string // the metadata branch's tip, as the fixture has pushed it
36 }
37
38 func newRecordFixture(t *testing.T) *recordFixture {
39 t.Helper()
40 dir := t.TempDir()
41 usersDB := filepath.Join(dir, "users.db")
42 if err := identity.Init(usersDB); err != nil {
43 t.Fatal(err)
44 }
45 ident, err := identity.Open(usersDB)
46 if err != nil {
47 t.Fatal(err)
48 }
49 ident.AuditPath = filepath.Join(dir, "audit.log")
50 defer ident.Close()
51
52 f := &recordFixture{t: t, dir: dir, usersDB: usersDB,
53 runsDB: filepath.Join(dir, "runs.db")}
54 for _, u := range []struct {
55 name string
56 into *string
57 }{
58 {"runner", &f.runnerID},
59 {"other", &f.otherID},
60 } {
61 _, pub := writeKey(t, filepath.Join(dir, u.name))
62 acct, err := ident.Create(u.name, pub, "test")
63 if err != nil {
64 t.Fatal(err)
65 }
66 *u.into = acct.Name
67 }
68
69 acls, err := access.Open(usersDB)
70 if err != nil {
71 t.Fatal(err)
72 }
73 acls.AuditPath = filepath.Join(dir, "audit.log")
74 t.Cleanup(func() { acls.Close() })
75 st := repo.New(filepath.Join(dir, "repos"), "/bin/true", acls)
76 m, err := st.Create("demo", f.runnerID, "")
77 if err != nil {
78 t.Fatal(err)
79 }
80 a, err := m.ACL()
81 if err != nil {
82 t.Fatal(err)
83 }
84 if err := a.SetEntry(f.otherID, "maintain"); err != nil {
85 t.Fatal(err)
86 }
87 a.SetRunners([]string{f.runnerID})
88 if err := m.SetACL(a, "test"); err != nil {
89 t.Fatal(err)
90 }
91 f.repo = m
92
93 queue, err := runs.Open(f.runsDB)
94 if err != nil {
95 t.Fatal(err)
96 }
97 defer queue.Close()
98 runID, err := id.New(time.Now().UTC())
99 if err != nil {
100 t.Fatal(err)
101 }
102 run := runs.Run{
103 ID: runID, Repo: "demo", Ref: "refs/heads/main", Branch: "main",
104 SHA: strings.Repeat("ab", 20), Workflow: ".vole/workflows/ci.yaml",
105 WorkflowSHA: strings.Repeat("cd", 20), Job: "build",
106 Steps: []runs.Step{{Name: "test", Run: "true"}}, Created: time.Now().UTC(),
107 }
108 if _, err := queue.Enqueue(run, nil); err != nil {
109 t.Fatal(err)
110 }
111 // The runner holds it, which is the only state in which a result may be reported.
112 leased, err := queue.Lease(runs.LeaseRequest{Owner: f.runnerID,
113 Repos: []string{"demo"}, TTL: time.Minute}, time.Now().UTC())
114 if err != nil || leased == nil {
115 t.Fatalf("leasing: %v %v", leased, err)
116 }
117 f.run = leased
118 return f
119 }
120
121 // writeKey makes an OpenSSH key pair and returns the private path and the public line.
122 func writeKey(t *testing.T, base string) (string, string) {
123 t.Helper()
124 if out, err := exec.Command("ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-C", "test",
125 "-f", base).CombinedOutput(); err != nil {
126 t.Fatalf("ssh-keygen: %v: %s", err, out)
127 }
128 pub, err := os.ReadFile(base + ".pub")
129 if err != nil {
130 t.Fatal(err)
131 }
132 return base, strings.TrimSpace(string(pub))
133 }
134
135 // record is a well-formed record for the fixture's leased job, with the attempt and
136 // signer the caller asks for.
137 func (f *recordFixture) record(attempt int, signer string) runs.Record {
138 f.t.Helper()
139 return runs.Record{
140 RunID: f.run.ID, Attempt: attempt, Repo: "demo", Ref: f.run.Ref, SHA: f.run.SHA,
141 Workflow: f.run.Workflow, WorkflowSHA: f.run.WorkflowSHA, Job: f.run.Job,
142 StepsSHA: f.run.StepsSHA, Exit: 0, Finished: time.Now().UTC(),
143 Signer: signer,
144 }
145 }
146
147 // push writes a record on top of the branch and returns the new tip.
148 func (f *recordFixture) push(rec runs.Record) string {
149 f.t.Helper()
150 g := f.repo.Git()
151 commit, err := runs.WriteRecord(g, metadata.Ref, rec, "runner", nil)
152 if err != nil {
153 f.t.Fatalf("writing the record: %v", err)
154 }
155 if err := g.UpdateRef(metadata.Ref, commit); err != nil {
156 f.t.Fatal(err)
157 }
158 old := f.current
159 f.current = commit
160 return old
161 }
162
163 // preReceive runs the gate for a push of the metadata branch by one account.
164 func (f *recordFixture) preReceive(actor, old, new string) (int, string) {
165 f.t.Helper()
166 if old == "" {
167 old = git.ZeroSHA
168 }
169 var out strings.Builder
170 code := PreReceive(Config{
171 RepoDir: f.repo.Dir,
172 Actor: actor,
173 UsersDB: f.usersDB,
174 RunsDB: f.runsDB,
175 In: strings.NewReader(old + " " + new + " " + metadata.Ref + "\n"),
176 Out: &out,
177 })
178 return code, out.String()
179 }
180
181 func TestRunRecordPushIsRefused(t *testing.T) {
182 f := newRecordFixture(t)
183 rec := f.record(f.run.Attempts, f.runnerID)
184 f.push(rec)
185
186 code, out := f.preReceive(f.runnerID, "", f.current)
187 if code == 0 {
188 t.Fatal("a run-record push was accepted; records belong in the database now")
189 }
190 if !strings.Contains(out, "stored in the instance database") {
191 t.Fatalf("the refusal does not name the new home:\n%s", out)
192 }
193 }