internal/store/commentmigrate.go
80 lines · 2466 bytes
3 symbols in this file
1package store
2
3import (
4 "fmt"
5 "regexp"
6)
7
8// commitRefPat matches the legacy commit-reference comment format, written
9// before these entries were system messages: "closed by commit <sha>: …"
10// or "referenced in commit <sha>: …" with a bare short sha.
11var commitRefPat = regexp.MustCompile(`^((?:closed by|referenced in) commit )([0-9a-f]{7,40})(: [\s\S]*)$`)
12
13// MigrateCommitRefComments converts legacy commit-reference comments into
14// system messages with a linked sha, matching what new references produce.
15// It is idempotent: only kind='comment' rows are considered, and converted
16// rows become kind='system'. Returns the number converted.
17func (s *Store) MigrateCommitRefComments() (int, error) {
18 n1, err := s.migrateRefTable(
19 "issue_comments", "issues", "issue_id",
20 "'closed by commit %' OR c.body LIKE 'referenced in commit %'")
21 if err != nil {
22 return n1, err
23 }
24 n2, err := s.migrateRefTable(
25 "mr_comments", "merge_requests", "mr_id",
26 "'closed by commit %' OR c.body LIKE 'referenced in commit %'")
27 return n1 + n2, err
28}
29
30func (s *Store) migrateRefTable(table, itemTable, fk, likeClause string) (int, error) {
31 q := fmt.Sprintf(`
32 SELECT c.id, c.body, COALESCE(u.username, o.name) || '/' || r.name
33 FROM %s c
34 JOIN %s it ON it.id = c.%s
35 JOIN repos r ON r.id = it.repo_id
36 LEFT JOIN users u ON r.owner_kind = 'user' AND u.id = r.owner_id
37 LEFT JOIN orgs o ON r.owner_kind = 'org' AND o.id = r.owner_id
38 WHERE c.kind = 'comment' AND (c.body LIKE %s)`, table, itemTable, fk, likeClause)
39 rows, err := s.DB.Query(q)
40 if err != nil {
41 return 0, err
42 }
43 type conv struct {
44 id int64
45 body string
46 }
47 var todo []conv
48 for rows.Next() {
49 var id int64
50 var body, path string
51 if err := rows.Scan(&id, &body, &path); err != nil {
52 rows.Close()
53 return 0, err
54 }
55 m := commitRefPat.FindStringSubmatch(body)
56 if m == nil {
57 continue // LIKE candidate that isn't the exact format
58 }
59 verb, sha, rest := m[1], m[2], m[3]
60 newBody := fmt.Sprintf("%s[%s](/%s/commit/%s)%s", verb, sha, path, sha, rest)
61 todo = append(todo, conv{id, newBody})
62 }
63 rows.Close()
64 if err := rows.Err(); err != nil {
65 return 0, err
66 }
67
68 tx, err := s.DB.Begin()
69 if err != nil {
70 return 0, err
71 }
72 defer tx.Rollback()
73 upd := fmt.Sprintf("UPDATE %s SET body = ?, kind = 'system' WHERE id = ?", table)
74 for _, c := range todo {
75 if _, err := tx.Exec(upd, c.body, c.id); err != nil {
76 return 0, err
77 }
78 }
79 return len(todo), tx.Commit()
80}