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 · 2h 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
ci/runs/runs.go +65
| @@ -54,6 +54,17 @@ const ( | |||
| 54 | 54 | CandidateRefRoot = "refs/vole/candidates/" | |
| 55 | 55 | ) | |
| 56 | 56 | ||
| 57 | // ValidStatus reports whether status is one a run row can hold. It is the check a | ||
| 58 | // caller uses before it turns a human-supplied filter into a query, so a typo reads | ||
| 59 | // as a usage mistake rather than an empty list. | ||
| 60 | func ValidStatus(status string) bool { | ||
| 61 | switch status { | ||
| 62 | case StatusQueued, StatusRunning, StatusSuccess, StatusFailed, StatusCanceled, StatusError: | ||
| 63 | return true | ||
| 64 | } | ||
| 65 | return false | ||
| 66 | } | ||
| 67 | |||
| 57 | 68 | type Step struct { | |
| 58 | 69 | Name string `json:"name"` | |
| 59 | 70 | Run string `json:"run"` | |
| @@ -1987,6 +1998,60 @@ func (s *Store) Recent(repo string, limit int) ([]*Run, error) { | |||
| 1987 | 1998 | return out, rows.Err() | |
| 1988 | 1999 | } | |
| 1989 | 2000 | ||
| 2001 | // SearchFilter narrows a run listing. Empty fields are no filter; SHA is a prefix, | ||
| 2002 | // which is how a command accepts the abbreviated commit a person copied rather than | ||
| 2003 | // demanding all forty characters. | ||
| 2004 | type SearchFilter struct { | ||
| 2005 | Status string | ||
| 2006 | Branch string | ||
| 2007 | SHA string | ||
| 2008 | Limit int | ||
| 2009 | } | ||
| 2010 | |||
| 2011 | // Search returns a repository's runs newest first, narrowed by filter. It is the | ||
| 2012 | // query behind `vole run list`: the same rows the runs page shows, without the | ||
| 2013 | // pager, because a command that diagnoses one build is not reading page six. | ||
| 2014 | func (s *Store) Search(repo string, f SearchFilter) ([]*Run, error) { | ||
| 2015 | if f.Limit < 1 { | ||
| 2016 | return nil, nil | ||
| 2017 | } | ||
| 2018 | query := `SELECT ` + runColumns + ` FROM runs WHERE repo = ?` | ||
| 2019 | args := []any{repo} | ||
| 2020 | if f.Status != "" { | ||
| 2021 | query += ` AND status = ?` | ||
| 2022 | args = append(args, f.Status) | ||
| 2023 | } | ||
| 2024 | if f.Branch != "" { | ||
| 2025 | query += ` AND branch = ?` | ||
| 2026 | args = append(args, f.Branch) | ||
| 2027 | } | ||
| 2028 | if f.SHA != "" { | ||
| 2029 | if len(f.SHA) == 40 { | ||
| 2030 | query += ` AND sha = ?` | ||
| 2031 | args = append(args, f.SHA) | ||
| 2032 | } else { | ||
| 2033 | query += ` AND sha LIKE ?` | ||
| 2034 | args = append(args, f.SHA+"%") | ||
| 2035 | } | ||
| 2036 | } | ||
| 2037 | query += ` ORDER BY created DESC LIMIT ?` | ||
| 2038 | args = append(args, f.Limit) | ||
| 2039 | rows, err := s.db.Query(query, args...) | ||
| 2040 | if err != nil { | ||
| 2041 | return nil, err | ||
| 2042 | } | ||
| 2043 | defer rows.Close() | ||
| 2044 | var out []*Run | ||
| 2045 | for rows.Next() { | ||
| 2046 | r, err := scanRun(rows) | ||
| 2047 | if err != nil { | ||
| 2048 | return nil, err | ||
| 2049 | } | ||
| 2050 | out = append(out, r) | ||
| 2051 | } | ||
| 2052 | return out, rows.Err() | ||
| 2053 | } | ||
| 2054 | |||
| 1990 | 2055 | // RecentPage returns one page of a repository's runs, newest first. status filters | |
| 1991 | 2056 | // to one value when it is not empty. A list page counts the rows first, so this | |
| 1992 | 2057 | // query takes the offset the pager already calculated instead of a limit alone. | |