vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Page through a repository's runs
31c03fc8909059f2b52a5fd9f7be65cebe6b31ec · Keenan Nemetz · 10d ago · parent 3516aef3
Page through a repository's runs The runs tab fetched one fixed window of 100 jobs, so older history could never be reached and a status filter only searched that window. The list now pages in the store, newest first, with one grouped count for the chips and the pager, and the status filter survives every page link.
ci/runs/runs.go +52
| @@ -1560,6 +1560,58 @@ func (s *Store) Recent(repo string, limit int) ([]*Run, error) { | |||
| 1560 | 1560 | return out, rows.Err() | |
| 1561 | 1561 | } | |
| 1562 | 1562 | ||
| 1563 | // RecentPage returns one page of a repository's runs, newest first. status filters | ||
| 1564 | // to one value when it is not empty. A list page counts the rows first, so this | ||
| 1565 | // query takes the offset the pager already calculated instead of a limit alone. | ||
| 1566 | func (s *Store) RecentPage(repo, status string, limit, offset int) ([]*Run, error) { | ||
| 1567 | if limit < 1 { | ||
| 1568 | return nil, nil | ||
| 1569 | } | ||
| 1570 | query := `SELECT ` + runColumns + ` FROM runs WHERE repo = ?` | ||
| 1571 | args := []any{repo} | ||
| 1572 | if status != "" { | ||
| 1573 | query += ` AND status = ?` | ||
| 1574 | args = append(args, status) | ||
| 1575 | } | ||
| 1576 | query += ` ORDER BY created DESC LIMIT ? OFFSET ?` | ||
| 1577 | args = append(args, limit, offset) | ||
| 1578 | rows, err := s.db.Query(query, args...) | ||
| 1579 | if err != nil { | ||
| 1580 | return nil, err | ||
| 1581 | } | ||
| 1582 | defer rows.Close() | ||
| 1583 | var out []*Run | ||
| 1584 | for rows.Next() { | ||
| 1585 | r, err := scanRun(rows) | ||
| 1586 | if err != nil { | ||
| 1587 | return nil, err | ||
| 1588 | } | ||
| 1589 | out = append(out, r) | ||
| 1590 | } | ||
| 1591 | return out, rows.Err() | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | // StatusCounts groups a repository's runs by status. The page sums the values for | ||
| 1595 | // the unfiltered total, so one query feeds every chip and the pager. A status with | ||
| 1596 | // no runs is absent; a caller treats a missing key as zero. | ||
| 1597 | func (s *Store) StatusCounts(repo string) (map[string]int, error) { | ||
| 1598 | rows, err := s.db.Query(`SELECT status, COUNT(*) FROM runs WHERE repo = ? GROUP BY status`, repo) | ||
| 1599 | if err != nil { | ||
| 1600 | return nil, err | ||
| 1601 | } | ||
| 1602 | defer rows.Close() | ||
| 1603 | out := map[string]int{} | ||
| 1604 | for rows.Next() { | ||
| 1605 | var status string | ||
| 1606 | var n int | ||
| 1607 | if err := rows.Scan(&status, &n); err != nil { | ||
| 1608 | return nil, err | ||
| 1609 | } | ||
| 1610 | out[status] = n | ||
| 1611 | } | ||
| 1612 | return out, rows.Err() | ||
| 1613 | } | ||
| 1614 | |||
| 1563 | 1615 | type scannable interface{ Scan(...any) error } | |
| 1564 | 1616 | ||
| 1565 | 1617 | // Held reports whether owner still has the lease for id. A finish from a runner | |