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.

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/status.go +41

@@ -7,6 +7,8 @@ package ssh
7 7
8 8 import (
9 9 "fmt"
10 "io"
11 "sort"
10 12 "strings"
11 13 "time"
12 14
@@ -50,6 +52,10 @@ func (s *Server) serveChangeStatus(ch xssh.Channel, argv []string, user, remote
50 52 fmt.Fprintf(ch, "vole: %v\n", err)
51 53 return 1
52 54 }
55 // The runs for the head are the ones a reviewer opens first, and they are true
56 // whether the change is open or already landed. The run id is the whole point:
57 // without it `vole run show` and `vole run logs` have nothing to take.
58 writeRunLines(ch, s.cfg.Runs, name, req.Head, "head")
53 59 if !req.Open() {
54 60 fmt.Fprintf(ch, "combined: not needed (%s)\n", req.State)
55 61 return 0
@@ -87,5 +93,40 @@ func (s *Server) serveChangeStatus(ch xssh.Channel, argv []string, user, remote
87 93 } else if len(plan.Gaps) > 0 {
88 94 fmt.Fprint(ch, "landing: waiting for the combined result\n")
89 95 }
96 // The combined result is a different commit once the base has moved, so its
97 // runs are not the head's. They are the ones that explain a failed combined
98 // line, and a fast-forward has no separate commit to ask about.
99 if plan.NewTip != "" && plan.NewTip != req.Head {
100 writeRunLines(ch, s.cfg.Runs, name, plan.NewTip, "combined")
101 }
90 102 return 0
91 103 }
104
105 // writeRunLines lists the newest run per job for one commit, in the same order the
106 // change page's Runs tab uses. It is display text, not a wire format: the id is what
107 // a reader carries to `vole run show`, and the status says whether that is the run to
108 // open. A missing queue is not an error here; the rest of the status still answers.
109 func writeRunLines(out io.Writer, store *runs.Store, repo, sha, where string) {
110 if store == nil || sha == "" {
111 return
112 }
113 byKey, err := store.ForSHA(repo, sha)
114 if err != nil {
115 return
116 }
117 keys := make([]string, 0, len(byKey))
118 for key := range byKey {
119 keys = append(keys, key)
120 }
121 sort.Strings(keys)
122 for _, key := range keys {
123 r := byKey[key]
124 exit := ""
125 switch r.Status {
126 case runs.StatusSuccess, runs.StatusFailed, runs.StatusCanceled, runs.StatusError:
127 exit = fmt.Sprintf(" exit %d", r.ExitCode)
128 }
129 fmt.Fprintf(out, "run: %s %s %s/%s%s (%s)\n",
130 r.ID, r.Status, r.Workflow, r.Job, exit, where)
131 }
132 }