mirror of
https://github.com/therootcompany/golib.git
synced 2026-11-04 06:55:00 +00:00
feat: expose refresh outcomes
This commit is contained in:
parent
c6da1e7e4a
commit
a43a722e79
@ -108,7 +108,10 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
state := "already up to date"
|
||||
if updated {
|
||||
switch updated {
|
||||
case gitshallow.UpdateSkipped:
|
||||
state = "skipped"
|
||||
case gitshallow.UpdateChanged:
|
||||
state = "updated"
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s (%s)\n", time.Since(t).Round(time.Millisecond), state)
|
||||
|
||||
@ -59,8 +59,8 @@ func TestRepo_ForcePushRecovery(t *testing.T) {
|
||||
repo := gitshallow.New(upstream, clone, 1, "main")
|
||||
if updated, err := repo.Update(t.Context()); err != nil {
|
||||
t.Fatalf("first Update: %v", err)
|
||||
} else if !updated {
|
||||
t.Fatal("first Update: expected updated=true")
|
||||
} else if updated != gitshallow.UpdateChanged {
|
||||
t.Fatal("first Update: expected changed")
|
||||
}
|
||||
got, err := os.ReadFile(filepath.Join(clone, "data.txt"))
|
||||
if err != nil {
|
||||
@ -89,8 +89,8 @@ func TestRepo_ForcePushRecovery(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("post-force-push Fetch: %v", err)
|
||||
}
|
||||
if !updated {
|
||||
t.Error("post-force-push Fetch: expected updated=true")
|
||||
if updated != gitshallow.UpdateChanged {
|
||||
t.Error("post-force-push Fetch: expected changed")
|
||||
}
|
||||
got, err = os.ReadFile(filepath.Join(clone, "data.txt"))
|
||||
if err != nil {
|
||||
|
||||
@ -221,18 +221,27 @@ func (r *Repo) gc(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateStatus describes what happened during an update.
|
||||
type UpdateStatus uint8
|
||||
|
||||
const (
|
||||
UpdateSkipped UpdateStatus = iota
|
||||
UpdateCurrent
|
||||
UpdateChanged
|
||||
)
|
||||
|
||||
// Fetch clones the repo if missing, pulls otherwise, and conditionally runs
|
||||
// GC based on GCInterval. Returns whether HEAD changed. Implements Upstream.
|
||||
// Safe to call concurrently — concurrent callers share a single in-flight
|
||||
// fetch (via singleflight) and all receive the same result. To force a
|
||||
// pull regardless of MaxAge, set MaxAge=-1 before calling.
|
||||
func (r *Repo) Update(ctx context.Context) (updated bool, err error) {
|
||||
func (r *Repo) Update(ctx context.Context) (UpdateStatus, error) {
|
||||
// MaxAge: file-mtime gate (FETCH_HEAD is rewritten on every successful
|
||||
// fetch, so its mtime is "last time we talked to the remote").
|
||||
if maxAge := r.effectiveMaxAge(); maxAge > 0 {
|
||||
if info, err := os.Stat(filepath.Join(r.Path, ".git", "FETCH_HEAD")); err == nil {
|
||||
if time.Since(info.ModTime()) < maxAge {
|
||||
return false, nil
|
||||
return UpdateSkipped, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -241,9 +250,12 @@ func (r *Repo) Update(ctx context.Context) (updated bool, err error) {
|
||||
return r.fetch(ctx)
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
return UpdateCurrent, err
|
||||
}
|
||||
return v.(bool), nil
|
||||
if v.(bool) {
|
||||
return UpdateChanged, nil
|
||||
}
|
||||
return UpdateCurrent, nil
|
||||
}
|
||||
|
||||
func (r *Repo) fetch(ctx context.Context) (bool, error) {
|
||||
|
||||
@ -40,8 +40,8 @@ func TestRepo_Clone(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Fetch (clone): %v", err)
|
||||
}
|
||||
if !updated {
|
||||
t.Error("fresh clone: expected updated=true")
|
||||
if updated != gitshallow.UpdateChanged {
|
||||
t.Error("fresh clone: expected UpdateChanged")
|
||||
}
|
||||
|
||||
for _, rel := range []string{
|
||||
@ -73,8 +73,8 @@ func TestRepo_Pull_SameInstance(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("second Fetch: %v", err)
|
||||
}
|
||||
if updated {
|
||||
t.Error("same-instance second pull: expected updated=false (already at HEAD)")
|
||||
if updated == gitshallow.UpdateChanged {
|
||||
t.Error("same-instance second pull: expected not UpdateChanged (already at HEAD)")
|
||||
}
|
||||
t.Log("same-instance pull correctly reported no update")
|
||||
}
|
||||
@ -94,8 +94,8 @@ func TestRepo_Pull_FreshInstance(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("fresh-instance Fetch: %v", err)
|
||||
}
|
||||
if updated {
|
||||
t.Error("fresh-instance pull: expected updated=false (HEAD unchanged on disk)")
|
||||
if updated == gitshallow.UpdateChanged {
|
||||
t.Error("fresh-instance pull: expected not UpdateChanged (HEAD unchanged on disk)")
|
||||
}
|
||||
t.Log("fresh-instance pull correctly reported no update")
|
||||
}
|
||||
|
||||
@ -21,3 +21,8 @@ require (
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/therootcompany/golib/net/gitshallow => ../gitshallow
|
||||
github.com/therootcompany/golib/sync/cacheable => ../../sync/cacheable
|
||||
)
|
||||
|
||||
@ -8,16 +8,12 @@ github.com/therootcompany/golib/io/transform/gsheet2csv v1.1.0 h1:R5zAPun5p3JPlN
|
||||
github.com/therootcompany/golib/io/transform/gsheet2csv v1.1.0/go.mod h1:CsntOAhhIMdzSAYuwuRZtfkdoiER9+TfSgYOIDWN71o=
|
||||
github.com/therootcompany/golib/net/dnsresolver v0.6.0 h1:sy08rzX/2QowofAKnCw1QtTDoatPL6/FXZcb+Wz4GlM=
|
||||
github.com/therootcompany/golib/net/dnsresolver v0.6.0/go.mod h1:tOV99lX6ghpWPksxSFAt4CDe5XLefFzIGf2qXsZJap4=
|
||||
github.com/therootcompany/golib/net/gitshallow v0.10.0 h1:cLN9ElTmNWk5HSbaRD3sSFuGdpgy4YXc+LVzjzT0nM4=
|
||||
github.com/therootcompany/golib/net/gitshallow v0.10.0/go.mod h1:S/3OK7wMOqJJjP79U9Qw/l3jjEyrb/zsjJ8HAPAX/Fc=
|
||||
github.com/therootcompany/golib/net/httpcache v0.6.0 h1:qFBnSs6rZrtUQ+fweP4bHF2FQd/Syvs7mRN5nO5YtkY=
|
||||
github.com/therootcompany/golib/net/httpcache v0.6.0/go.mod h1:mKSQJWpGSCDBzWkTCwT7dO1SCVCsFmdkRgKr39fNS1U=
|
||||
github.com/therootcompany/golib/net/ipcohort v0.9.0 h1:j3AaoW2u4XQJz1ya6fixv3pb3KgxIpZV8t0082/kS3E=
|
||||
github.com/therootcompany/golib/net/ipcohort v0.9.0/go.mod h1:Ljcnt2xOryVDGIVV5Kzt5hj2LhW4lBAWo5OeeVYU9ys=
|
||||
github.com/therootcompany/golib/net/iplist v0.9.2 h1:2WGFYzLhDyeGRuagLPe6Hp6gWMNqfqWpei9KG41C/9E=
|
||||
github.com/therootcompany/golib/net/iplist v0.9.2/go.mod h1:TJ+0RP4M8/zkjdm+D9YXvDUJL2VZiC2VpnZJiK9Bfjk=
|
||||
github.com/therootcompany/golib/sync/cacheable v0.1.0 h1:s6mz4dZv9Vsd0nuDz8IpS+n35GLjOwpjZlomimkqw1M=
|
||||
github.com/therootcompany/golib/sync/cacheable v0.1.0/go.mod h1:0ZQ9sOtqyr1j8ZfSaAyPM1HlGi1B2qFgaGC4ULUbhD4=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
|
||||
@ -216,7 +216,7 @@ func (ps *IPPrefixSet) Contains(addr netip.Addr) bool {
|
||||
}
|
||||
|
||||
func (ps *IPPrefixSet) reload(ctx context.Context) (*ipcohort.Cohort, error) {
|
||||
updated, err := ps.repo.Update(ctx)
|
||||
status, err := ps.repo.Update(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -226,7 +226,7 @@ func (ps *IPPrefixSet) reload(ctx context.Context) (*ipcohort.Cohort, error) {
|
||||
paths[i] = ps.repo.FilePath(f)
|
||||
}
|
||||
|
||||
if cachedCohortValid(updated, ps.cohort.Load(), paths) {
|
||||
if cachedCohortValid(status, ps.cohort.Load(), paths) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -235,15 +235,14 @@ func (ps *IPPrefixSet) reload(ctx context.Context) (*ipcohort.Cohort, error) {
|
||||
return nil, fmt.Errorf("load files: %w", err)
|
||||
}
|
||||
|
||||
log().Info("prefix set loaded", "entries", commaify(cohort.Size()))
|
||||
return cohort, nil
|
||||
}
|
||||
|
||||
// cachedCohortValid reports whether the current cohort can be reused
|
||||
// without reloading from disk: the repo was not updated, the cohort is
|
||||
// non-nil and non-empty, and all data files are present.
|
||||
func cachedCohortValid(updated bool, cohort *ipcohort.Cohort, paths []string) bool {
|
||||
return !updated && cohort != nil && cohort.Size() > 0 && filesPresent(paths)
|
||||
func cachedCohortValid(status gitshallow.UpdateStatus, cohort *ipcohort.Cohort, paths []string) bool {
|
||||
return status != gitshallow.UpdateChanged && cohort != nil && cohort.Size() > 0 && filesPresent(paths)
|
||||
}
|
||||
|
||||
func filesPresent(paths []string) bool {
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/therootcompany/golib/net/gitshallow"
|
||||
"github.com/therootcompany/golib/net/ipcohort"
|
||||
)
|
||||
|
||||
@ -32,16 +33,16 @@ func TestCachedCohortValidRequiresFiles(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !cachedCohortValid(false, cohort, []string{present}) {
|
||||
if !cachedCohortValid(gitshallow.UpdateCurrent, cohort, []string{present}) {
|
||||
t.Fatal("fresh metadata and present files should use the cache")
|
||||
}
|
||||
if cachedCohortValid(false, cohort, []string{present, filepath.Join(dir, "gone")}) {
|
||||
if cachedCohortValid(gitshallow.UpdateCurrent, cohort, []string{present, filepath.Join(dir, "gone")}) {
|
||||
t.Fatal("missing data file must invalidate the cache")
|
||||
}
|
||||
if cachedCohortValid(false, nil, []string{present}) {
|
||||
if cachedCohortValid(gitshallow.UpdateCurrent, nil, []string{present}) {
|
||||
t.Fatal("nil cohort must not be used")
|
||||
}
|
||||
if cachedCohortValid(true, cohort, []string{present}) {
|
||||
if cachedCohortValid(gitshallow.UpdateChanged, cohort, []string{present}) {
|
||||
t.Fatal("updated repo must reload")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user