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/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