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.
Read a run and its log from the CLI
92d8afc86ab7a5e8f2e56a8981e0614aa55980f1 · Keenan Nemetz · 3h ago · parent 4b8c9e1d
Read a run and its log from the CLI The queue and its logs live on the instance, so the CLI now has read verbs beside the cancel and rerun writes: run list, run show and run logs. They check the repository's read capability, scope an id to its repository, and stream a log to stdout with refusals on stderr. change show lists the runs for the head and, when the landing needs one, the combined result, so a red check carries the id needed to open it. The queue search narrows by status, branch and commit prefix without a pager. closes: 01M6YZAQZVJWTA9BA5RV2H6PQ8
server/ssh/sshserver_test.go +6−2
| @@ -18,6 +18,7 @@ import ( | |||
| 18 | 18 | ||
| 19 | 19 | xssh "golang.org/x/crypto/ssh" | |
| 20 | 20 | ||
| 21 | "rodent.software/vole/ci/logs" | ||
| 21 | 22 | "rodent.software/vole/ci/runs" | |
| 22 | 23 | "rodent.software/vole/core/lfs" | |
| 23 | 24 | "rodent.software/vole/forge/access" | |
| @@ -77,7 +78,8 @@ type harness struct { | |||
| 77 | 78 | roKey string // private key file for the read-only user | |
| 78 | 79 | repoName string | |
| 79 | 80 | runs *runs.Store // the CI queue the runner verb serves | |
| 80 | objects *lfs.Store // where a run's log and artifacts are checked for | ||
| 81 | logs *logs.Store // the attempt logs the run verbs serve | ||
| 82 | objects *lfs.Store // where a run's artifacts are checked for | ||
| 81 | 83 | writerID string // the account id the writer's key belongs to | |
| 82 | 84 | } | |
| 83 | 85 | ||
| @@ -147,12 +149,14 @@ func newHarnessWith(t *testing.T, tweak func(*Config)) *harness { | |||
| 147 | 149 | } | |
| 148 | 150 | t.Cleanup(func() { queue.Close() }) | |
| 149 | 151 | objects := lfs.New(filepath.Join(dir, "objects"), 1<<20) | |
| 152 | logStore := logs.New(filepath.Join(dir, "logs")) | ||
| 150 | 153 | ||
| 151 | 154 | log := slog.New(slog.NewTextHandler(io.Discard, nil)) | |
| 152 | 155 | cfg := Config{ | |
| 153 | 156 | Identity: ident, | |
| 154 | 157 | Repos: rs, | |
| 155 | 158 | Runs: queue, | |
| 159 | Logs: logStore, | ||
| 156 | 160 | LFS: objects, | |
| 157 | 161 | Log: log, | |
| 158 | 162 | HostKeyPath: filepath.Join(dir, "host_key"), | |
| @@ -175,7 +179,7 @@ func newHarnessWith(t *testing.T, tweak func(*Config)) *harness { | |||
| 175 | 179 | return &harness{ | |
| 176 | 180 | t: t, dir: dir, addr: ln.Addr().String(), ident: ident, repos: rs, | |
| 177 | 181 | srv: srv, keyPath: writer, roKey: reader, repoName: "demo", | |
| 178 | runs: queue, objects: objects, writerID: writerID, | ||
| 182 | runs: queue, logs: logStore, objects: objects, writerID: writerID, | ||
| 179 | 183 | } | |
| 180 | 184 | } | |
| 181 | 185 | ||