From cbb4dd8a3b3e388c4b6b26a9df627e8d29c3880d Mon Sep 17 00:00:00 2001 From: Amolith Date: Thu, 16 Oct 2025 13:57:06 -0600 Subject: [PATCH] fix(web/bugs): improve cache management Implements: bug-3823b04 Co-authored-by: Crush --- pkg/backend/hooks.go | 91 ++++++++++++++++++++++++++++++++++++++++++- pkg/web/webui_bugs.go | 12 +++++- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/pkg/backend/hooks.go b/pkg/backend/hooks.go index f6c64467100ac3f3eddb5e8384a787b6b7008c44..80f6584962e22536012beed4bb5d294015c1477a 100644 --- a/pkg/backend/hooks.go +++ b/pkg/backend/hooks.go @@ -2,15 +2,22 @@ package backend import ( "context" + "fmt" "io" "os" + "path/filepath" + "strings" "sync" + "github.com/charmbracelet/log/v2" "github.com/charmbracelet/soft-serve/git" + "github.com/charmbracelet/soft-serve/pkg/config" "github.com/charmbracelet/soft-serve/pkg/hooks" "github.com/charmbracelet/soft-serve/pkg/proto" "github.com/charmbracelet/soft-serve/pkg/sshutils" "github.com/charmbracelet/soft-serve/pkg/webhook" + bugCache "github.com/git-bug/git-bug/cache" + "github.com/git-bug/git-bug/repository" ) var _ hooks.Hooks = (*Backend)(nil) @@ -18,8 +25,24 @@ var _ hooks.Hooks = (*Backend)(nil) // PostReceive is called by the git post-receive hook. // // It implements Hooks. -func (d *Backend) PostReceive(_ context.Context, _ io.Writer, _ io.Writer, repo string, args []hooks.HookArg) { +func (d *Backend) PostReceive(ctx context.Context, _ io.Writer, _ io.Writer, repo string, args []hooks.HookArg) { d.logger.Debug("post-receive hook called", "repo", repo, "args", args) + + hasGitBugRefs := false + for _, arg := range args { + if isGitBugRef(arg.RefName) { + hasGitBugRefs = true + d.logger.Info("git-bug ref detected, rebuilding cache", "repo", repo, "ref", arg.RefName) + if err := rebuildGitBugCache(d.cfg, repo, d.logger); err != nil { + d.logger.Error("failed to rebuild git-bug cache", "repo", repo, "err", err) + } + break + } + } + + if !hasGitBugRefs { + d.logger.Debug("no git-bug refs detected in push", "repo", repo) + } } // PreReceive is called by the git pre-receive hook. @@ -132,3 +155,69 @@ func populateLastModified(ctx context.Context, d *Backend, name string) error { return rr.writeLastModified(c) } + +func isGitBugRef(refName string) bool { + return strings.HasPrefix(refName, "refs/bugs/") || strings.HasPrefix(refName, "refs/identities/") +} + +func rebuildGitBugCache(cfg *config.Config, repoName string, logger *log.Logger) error { + logger.Info("starting git-bug cache rebuild", "repo", repoName) + repoPath := filepath.Join(cfg.DataPath, "repos", repoName+".git") + + goGitRepo, err := repository.OpenGoGitRepo(repoPath, "git-bug", nil) + if err != nil { + return fmt.Errorf("open go-git repo: %w", err) + } + defer goGitRepo.Close() + + rc, err := bugCache.NewRepoCacheNoEvents(goGitRepo) + if err != nil { + return fmt.Errorf("create repo cache: %w", err) + } + defer func() { + if closeErr := rc.Close(); closeErr != nil { + logger.Error("failed to close git-bug cache", "repo", repoName, "err", closeErr) + } + }() + + var identityTotal int64 + logger.Debug("rebuilding identities cache", "repo", repoName) + for ev := range rc.Identities().Build() { + if ev.Err != nil { + return fmt.Errorf("rebuild identities cache: %w", ev.Err) + } + logger.Debug("identity build event", + "repo", repoName, + "event", ev.Event, + "typename", ev.Typename, + "total", ev.Total, + "progress", ev.Progress) + + if ev.Total > 0 { + identityTotal = ev.Total + } + } + logger.Info("identities cache rebuilt", "repo", repoName, "count", identityTotal) + + var bugTotal int64 + logger.Debug("rebuilding bugs cache", "repo", repoName) + for ev := range rc.Bugs().Build() { + if ev.Err != nil { + return fmt.Errorf("rebuild bugs cache: %w", ev.Err) + } + logger.Debug("bug build event", + "repo", repoName, + "event", ev.Event, + "typename", ev.Typename, + "total", ev.Total, + "progress", ev.Progress) + + if ev.Total > 0 { + bugTotal = ev.Total + } + } + logger.Info("bugs cache rebuilt", "repo", repoName, "count", bugTotal) + + logger.Info("git-bug cache rebuild complete", "repo", repoName, "identities", identityTotal, "bugs", bugTotal) + return nil +} diff --git a/pkg/web/webui_bugs.go b/pkg/web/webui_bugs.go index f889eec6db167e8cb740e8be5f2bce9807d7d9a3..54aa677a10a9e1219761e1136e48cf6605194cce 100644 --- a/pkg/web/webui_bugs.go +++ b/pkg/web/webui_bugs.go @@ -316,7 +316,11 @@ func repoBugs(w http.ResponseWriter, r *http.Request) { renderNotFound(w, r) return } - defer rc.Close() + defer func() { + if closeErr := rc.Close(); closeErr != nil { + logger.Debug("failed to close bug cache", "repo", repo.Name(), "err", closeErr) + } + }() status := r.URL.Query().Get("status") if status == "" { @@ -404,7 +408,11 @@ func repoBug(w http.ResponseWriter, r *http.Request) { renderNotFound(w, r) return } - defer rc.Close() + defer func() { + if closeErr := rc.Close(); closeErr != nil { + logger.Debug("failed to close bug cache", "repo", repo.Name(), "err", closeErr) + } + }() bugCache, err := findBugByHash(rc, hash) if err != nil {