internal/store/deps.go
142 lines · 3943 bytes
11 symbols in this file
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}