internal/store/deps.go

185f434bf23123c61a922129270c2db4cfebd493
gitbay/internal/store/deps.go history · blame · raw

142 lines · 3943 bytes

  1package store
  2
  3import (
  4	"database/sql"
  5	"errors"
  6)
  7
  8// BotUsername authors dependency-update issues. The account exists from
  9// migration 0028 with no key and no email: it authors, it never
 10// authenticates.
 11const BotUsername = "gitbay-bot"
 12
 13// DepCheck is a repo's opt-in dependency sweep state. A row exists only
 14// while checking is enabled.
 15type DepCheck struct {
 16	RepoID      int64
 17	LastCheck   string
 18	LastError   string
 19	IssueNumber int64 // 0 until an issue has been opened
 20}
 21
 22// DepReport is one dependency found to be behind, as last reported.
 23type DepReport struct {
 24	Ecosystem string
 25	Name      string
 26	Current   string
 27	Latest    string
 28}
 29
 30func (s *Store) EnableDepCheck(repoID int64) error {
 31	_, err := s.DB.Exec("INSERT OR IGNORE INTO dep_checks (repo_id) VALUES (?)", repoID)
 32	return err
 33}
 34
 35// DisableDepCheck stops checking and forgets what was reported, so
 36// re-enabling reports the current state afresh.
 37func (s *Store) DisableDepCheck(repoID int64) error {
 38	tx, err := s.DB.Begin()
 39	if err != nil {
 40		return err
 41	}
 42	defer tx.Rollback()
 43	if _, err := tx.Exec("DELETE FROM dep_reports WHERE repo_id = ?", repoID); err != nil {
 44		return err
 45	}
 46	if _, err := tx.Exec("DELETE FROM dep_checks WHERE repo_id = ?", repoID); err != nil {
 47		return err
 48	}
 49	return tx.Commit()
 50}
 51
 52func (s *Store) DepCheckFor(repoID int64) (DepCheck, error) {
 53	var d DepCheck
 54	err := s.DB.QueryRow(
 55		"SELECT repo_id, last_check, last_error, issue_number FROM dep_checks WHERE repo_id = ?", repoID).
 56		Scan(&d.RepoID, &d.LastCheck, &d.LastError, &d.IssueNumber)
 57	if errors.Is(err, sql.ErrNoRows) {
 58		return d, ErrNotFound
 59	}
 60	return d, err
 61}
 62
 63// DueDepChecks returns repos whose last check is older than
 64// intervalSeconds. Archived repos are skipped: nobody is going to act on
 65// the issue.
 66func (s *Store) DueDepChecks(intervalSeconds int) ([]Repo, error) {
 67	rows, err := s.DB.Query(repoSelect+`
 68		JOIN dep_checks d ON d.repo_id = r.id
 69		WHERE d.last_check = ''
 70		   OR strftime('%s','now') - strftime('%s', d.last_check) > ?
 71		ORDER BY r.id`, intervalSeconds)
 72	if err != nil {
 73		return nil, err
 74	}
 75	defer rows.Close()
 76	var out []Repo
 77	for rows.Next() {
 78		r, err := scanRepo(rows)
 79		if err != nil {
 80			return nil, err
 81		}
 82		if r.Settings.Archived {
 83			continue
 84		}
 85		out = append(out, r)
 86	}
 87	return out, rows.Err()
 88}
 89
 90// SetDepCheckResult stamps a sweep. An empty checkErr records success.
 91func (s *Store) SetDepCheckResult(repoID int64, checkErr string) error {
 92	_, err := s.DB.Exec(`
 93		UPDATE dep_checks SET last_error = ?,
 94		       last_check = strftime('%Y-%m-%dT%H:%M:%fZ','now')
 95		WHERE repo_id = ?`, checkErr, repoID)
 96	return err
 97}
 98
 99func (s *Store) SetDepIssue(repoID, number int64) error {
100	_, err := s.DB.Exec("UPDATE dep_checks SET issue_number = ? WHERE repo_id = ?", number, repoID)
101	return err
102}
103
104func (s *Store) ReportedDeps(repoID int64) ([]DepReport, error) {
105	rows, err := s.DB.Query(`
106		SELECT ecosystem, name, current, latest FROM dep_reports
107		WHERE repo_id = ? ORDER BY ecosystem, name`, repoID)
108	if err != nil {
109		return nil, err
110	}
111	defer rows.Close()
112	var out []DepReport
113	for rows.Next() {
114		var d DepReport
115		if err := rows.Scan(&d.Ecosystem, &d.Name, &d.Current, &d.Latest); err != nil {
116			return nil, err
117		}
118		out = append(out, d)
119	}
120	return out, rows.Err()
121}
122
123// ReplaceDepReports swaps in the current outdated set wholesale: a
124// dependency that was updated, removed, or renamed leaves no trace.
125func (s *Store) ReplaceDepReports(repoID int64, reports []DepReport) error {
126	tx, err := s.DB.Begin()
127	if err != nil {
128		return err
129	}
130	defer tx.Rollback()
131	if _, err := tx.Exec("DELETE FROM dep_reports WHERE repo_id = ?", repoID); err != nil {
132		return err
133	}
134	for _, d := range reports {
135		if _, err := tx.Exec(`
136			INSERT INTO dep_reports (repo_id, ecosystem, name, current, latest)
137			VALUES (?, ?, ?, ?, ?)`, repoID, d.Ecosystem, d.Name, d.Current, d.Latest); err != nil {
138			return err
139		}
140	}
141	return tx.Commit()
142}