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.

Index the rest of the metadata branch

7f547ca3b8da218249120d3c910c73c9a1a1e52d · Keenan Nemetz · 10d ago · parent c9202f7f

Index the rest of the metadata branch

The index held issues and their numbers. Everything else on the branch —
change requests, their comments, approvals and closes, and release
artifacts — was read from git on every page that needed it, and the
repository header read the whole requests subtree just to print a count.

Add the tables and derive them from the same commit:

- requests, with the body preserved for search;
- request_closes and request_approvals, each in its own table so a
  backlink and a current-approval count are queries rather than a walk of
  every request;
- comments for both kinds of entity, with the anchor a review comment
  carries;
- artifacts, the release pointers, with the size from the pointer.

The refresh is still driven by the path delta between two commits: a
changed issue or request replaces its own rows, its comments and its sets,
and a deleted one removes them while keeping its `#42` number. Rebuild and
the first read go through IndexRepo, which is the definition the
incremental path has to reproduce; a rebuild now clears the derived tables
first so a deleted repository leaves nothing behind.

Schema version 4 drops the old rows on first open, which is the existing
recovery path: the index is disposable and built from the branch.

forge/index/artifacts.go +57

@@ -0,0 +1,57 @@
1 package index
2
3 // Release artifacts on the metadata branch: releases/<tag>/<name> holds a git-lfs
4 // pointer, and the pointer is the record. The index stores the pointer's name,
5 // object id and size, which is what a release page lists and what a download
6 // needs before it asks the LFS store for the bytes.
7 //
8 // This is the one part of the branch whose authoritative copy is a tag, not a
9 // file: a tag can be deleted and recreated. The rows are replaced from the tree
10 // whenever the releases/ path changes, so they always describe the current
11 // branch.
12
13 import (
14 "github.com/rodent-software/vole/metadata"
15 )
16
17 // replaceArtifacts replaces every artifact row for a repository. Artifacts are a
18 // small, whole set: a release page shows a tag's files and the cost of rewriting
19 // the set is one statement when releases/ changes.
20 func replaceArtifacts(tx execer, repoID string, byTag map[string][]metadata.Artifact) error {
21 if _, err := tx.Exec(`DELETE FROM artifacts WHERE repo_id=?`, repoID); err != nil {
22 return err
23 }
24 for tag, list := range byTag {
25 for _, a := range list {
26 if _, err := tx.Exec(
27 `INSERT OR REPLACE INTO artifacts(repo_id, tag, name, oid, size) VALUES(?,?,?,?,?)`,
28 repoID, tag, a.Name, a.OID, a.Size); err != nil {
29 return err
30 }
31 }
32 }
33 return nil
34 }
35
36 // Artifacts returns the files attached to one tag, sorted by name. A tag with
37 // nothing attached is an empty list, which is also what a tag that predates the
38 // index's first read returns; the release page shows the same "no artifacts"
39 // state either way.
40 func (s *Store) Artifacts(repoID, tag string) ([]metadata.Artifact, error) {
41 rows, err := s.db.Query(
42 `SELECT name, oid, size FROM artifacts WHERE repo_id=? AND tag=? ORDER BY name`,
43 repoID, tag)
44 if err != nil {
45 return nil, err
46 }
47 defer rows.Close()
48 var out []metadata.Artifact
49 for rows.Next() {
50 var a metadata.Artifact
51 if err := rows.Scan(&a.Name, &a.OID, &a.Size); err != nil {
52 return nil, err
53 }
54 out = append(out, a)
55 }
56 return out, rows.Err()
57 }