@@ -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
+}
@@ -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 {