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.

Give the CLI rerun too

70291860685b74018f96a47686e5e6160e11f14f · Keenan Nemetz · 6d ago · parent deb024d4

Give the CLI rerun too

The run page offered Rerun while the CLI did not. It is the same maintainer
action against the same instance-side queue: `vole run rerun <id>` queues a
finished run again with the job it already pinned, refuses one that has not
finished, and writes the same `run.rerun` audit record the page writes.

Both transports now offer both run actions, which is what #168 asked of issues
and comments applied to CI.

server/ssh/cancel_test.go → server/ssh/run_test.go +82−5

@@ -1,9 +1,10 @@
1 1 package ssh
2 2
3 // A stop request reaches a running runner on its next heartbeat and nowhere else.
4 // These tests speak the runner protocol over a real SSH channel, because the reply
5 // is the contract: a runner stops when the server says so, and the attempt it sends
6 // back keeps the canceled status.
3 // The run actions the browser offers are also CLI verbs, and both go through the
4 // same rules. These tests speak the runner protocol and the run-management verbs
5 // over real SSH channels: a stop request reaches a running runner on its next
6 // heartbeat, the stopped attempt keeps the canceled status, and a finished run can
7 // be queued again.
7 8
8 9 import (
9 10 "os"
@@ -121,7 +122,7 @@ func TestTheCLIWillNotCancelAsAReader(t *testing.T) {
121 122 if err == nil {
122 123 t.Fatalf("a reader canceled a run: %s", out)
123 124 }
124 if !strings.Contains(string(out), "may not cancel runs") {
125 if !strings.Contains(string(out), "may not manage runs") {
125 126 t.Fatalf("refusal = %q, want the missing capability", out)
126 127 }
127 128 got, err := h.runs.Get(run.ID)
@@ -132,3 +133,79 @@ func TestTheCLIWillNotCancelAsAReader(t *testing.T) {
132 133 t.Fatalf("a reader's cancel was applied: %+v", got)
133 134 }
134 135 }
136
137 // Rerun is the other action on a run page, and the CLI offers it with the same
138 // rule: a finished run is queued again with the job it already pinned, and the
139 // history of the attempt is kept.
140 func TestTheCLIRerunsAFinishedRun(t *testing.T) {
141 t.Parallel()
142 h := newHarness(t)
143 runner := h.runner(t, h.keyPath)
144 run, _ := h.leaseRun(t, runner, "build")
145 if ok, err := h.runs.Finish(run.ID, h.writerID, 1, time.Now().UTC()); err != nil || !ok {
146 t.Fatalf("finish = %t, %v", ok, err)
147 }
148
149 client := h.dial(t, h.keyPath)
150 sess, err := client.NewSession()
151 if err != nil {
152 t.Fatalf("new session: %v", err)
153 }
154 defer sess.Close()
155 out, err := sess.CombinedOutput("vole-run-rerun /demo.git " + run.ID)
156 if err != nil {
157 t.Fatalf("rerun: %v: %s", err, out)
158 }
159 if !strings.Contains(string(out), "queued") {
160 t.Fatalf("rerun said %q, want a queued outcome", out)
161 }
162
163 got, err := h.runs.Get(run.ID)
164 if err != nil {
165 t.Fatal(err)
166 }
167 if got.Status != runs.StatusQueued || got.Attempts != run.Attempts {
168 t.Fatalf("run after the CLI rerun = %+v, want queued at the same attempt", got)
169 }
170
171 raw, err := os.ReadFile(filepath.Join(h.dir, "audit.log"))
172 if err != nil {
173 t.Fatalf("reading the audit log: %v", err)
174 }
175 audit := string(raw)
176 for _, want := range []string{"run.rerun", h.writerID, h.repoName, run.ID} {
177 if !strings.Contains(audit, want) {
178 t.Fatalf("audit log does not name %q:\n%s", want, audit)
179 }
180 }
181 }
182
183 // A run that has not finished cannot be rerun: that would run the same job twice,
184 // not queue it again.
185 func TestTheCLIWillNotRerunARunningRun(t *testing.T) {
186 t.Parallel()
187 h := newHarness(t)
188 runner := h.runner(t, h.keyPath)
189 run, _ := h.leaseRun(t, runner, "build")
190
191 client := h.dial(t, h.keyPath)
192 sess, err := client.NewSession()
193 if err != nil {
194 t.Fatalf("new session: %v", err)
195 }
196 defer sess.Close()
197 out, err := sess.CombinedOutput("vole-run-rerun /demo.git " + run.ID)
198 if err == nil {
199 t.Fatalf("a running run was rerun: %s", out)
200 }
201 if !strings.Contains(string(out), "only a finished run can be rerun") {
202 t.Fatalf("refusal = %q, want the finished-run rule", out)
203 }
204 got, err := h.runs.Get(run.ID)
205 if err != nil {
206 t.Fatal(err)
207 }
208 if got.Status != runs.StatusRunning {
209 t.Fatalf("a refused rerun changed the run: %+v", got)
210 }
211 }