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