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 · 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.