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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
state := "already up to date"
|
state := "already up to date"
|
||||||
if updated {
|
switch updated {
|
||||||
|
case gitshallow.UpdateSkipped:
|
||||||
|
state = "skipped"
|
||||||
|
case gitshallow.UpdateChanged:
|
||||||
state = "updated"
|
state = "updated"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "%s (%s)\n", time.Since(t).Round(time.Millisecond), state)
|
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")
|
repo := gitshallow.New(upstream, clone, 1, "main")
|
||||||
if updated, err := repo.Update(t.Context()); err != nil {
|
if updated, err := repo.Update(t.Context()); err != nil {
|
||||||
t.Fatalf("first Update: %v", err)
|
t.Fatalf("first Update: %v", err)
|
||||||
} else if !updated {
|
} else if updated != gitshallow.UpdateChanged {
|
||||||
t.Fatal("first Update: expected updated=true")
|
t.Fatal("first Update: expected changed")
|
||||||
}
|
}
|
||||||
got, err := os.ReadFile(filepath.Join(clone, "data.txt"))
|
got, err := os.ReadFile(filepath.Join(clone, "data.txt"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,8 +89,8 @@ func TestRepo_ForcePushRecovery(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("post-force-push Fetch: %v", err)
|
t.Fatalf("post-force-push Fetch: %v", err)
|
||||||
}
|
}
|
||||||
if !updated {
|
if updated != gitshallow.UpdateChanged {
|
||||||
t.Error("post-force-push Fetch: expected updated=true")
|
t.Error("post-force-push Fetch: expected changed")
|
||||||
}
|
}
|
||||||
got, err = os.ReadFile(filepath.Join(clone, "data.txt"))
|
got, err = os.ReadFile(filepath.Join(clone, "data.txt"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -221,18 +221,27 @@ func (r *Repo) gc(ctx context.Context) error {
|
|||||||
return err
|
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
|
// Fetch clones the repo if missing, pulls otherwise, and conditionally runs
|
||||||
// GC based on GCInterval. Returns whether HEAD changed. Implements Upstream.
|
// GC based on GCInterval. Returns whether HEAD changed. Implements Upstream.
|
||||||
// Safe to call concurrently — concurrent callers share a single in-flight
|
// Safe to call concurrently — concurrent callers share a single in-flight
|
||||||
// fetch (via singleflight) and all receive the same result. To force a
|
// fetch (via singleflight) and all receive the same result. To force a
|
||||||
// pull regardless of MaxAge, set MaxAge=-1 before calling.
|
// 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
|
// MaxAge: file-mtime gate (FETCH_HEAD is rewritten on every successful
|
||||||
// fetch, so its mtime is "last time we talked to the remote").
|
// fetch, so its mtime is "last time we talked to the remote").
|
||||||
if maxAge := r.effectiveMaxAge(); maxAge > 0 {
|
if maxAge := r.effectiveMaxAge(); maxAge > 0 {
|
||||||
if info, err := os.Stat(filepath.Join(r.Path, ".git", "FETCH_HEAD")); err == nil {
|
if info, err := os.Stat(filepath.Join(r.Path, ".git", "FETCH_HEAD")); err == nil {
|
||||||
if time.Since(info.ModTime()) < maxAge {
|
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)
|
return r.fetch(ctx)
|
||||||
})
|
})
|
||||||
if err != nil {
|
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) {
|
func (r *Repo) fetch(ctx context.Context) (bool, error) {
|
||||||
|
|||||||
@ -40,8 +40,8 @@ func TestRepo_Clone(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Fetch (clone): %v", err)
|
t.Fatalf("Fetch (clone): %v", err)
|
||||||
}
|
}
|
||||||
if !updated {
|
if updated != gitshallow.UpdateChanged {
|
||||||
t.Error("fresh clone: expected updated=true")
|
t.Error("fresh clone: expected UpdateChanged")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rel := range []string{
|
for _, rel := range []string{
|
||||||
@ -73,8 +73,8 @@ func TestRepo_Pull_SameInstance(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("second Fetch: %v", err)
|
t.Fatalf("second Fetch: %v", err)
|
||||||
}
|
}
|
||||||
if updated {
|
if updated == gitshallow.UpdateChanged {
|
||||||
t.Error("same-instance second pull: expected updated=false (already at HEAD)")
|
t.Error("same-instance second pull: expected not UpdateChanged (already at HEAD)")
|
||||||
}
|
}
|
||||||
t.Log("same-instance pull correctly reported no update")
|
t.Log("same-instance pull correctly reported no update")
|
||||||
}
|
}
|
||||||
@ -94,8 +94,8 @@ func TestRepo_Pull_FreshInstance(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("fresh-instance Fetch: %v", err)
|
t.Fatalf("fresh-instance Fetch: %v", err)
|
||||||
}
|
}
|
||||||
if updated {
|
if updated == gitshallow.UpdateChanged {
|
||||||
t.Error("fresh-instance pull: expected updated=false (HEAD unchanged on disk)")
|
t.Error("fresh-instance pull: expected not UpdateChanged (HEAD unchanged on disk)")
|
||||||
}
|
}
|
||||||
t.Log("fresh-instance pull correctly reported no update")
|
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/sys v0.38.0 // indirect
|
||||||
golang.org/x/tools v0.39.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/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 h1:sy08rzX/2QowofAKnCw1QtTDoatPL6/FXZcb+Wz4GlM=
|
||||||
github.com/therootcompany/golib/net/dnsresolver v0.6.0/go.mod h1:tOV99lX6ghpWPksxSFAt4CDe5XLefFzIGf2qXsZJap4=
|
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 h1:qFBnSs6rZrtUQ+fweP4bHF2FQd/Syvs7mRN5nO5YtkY=
|
||||||
github.com/therootcompany/golib/net/httpcache v0.6.0/go.mod h1:mKSQJWpGSCDBzWkTCwT7dO1SCVCsFmdkRgKr39fNS1U=
|
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 h1:j3AaoW2u4XQJz1ya6fixv3pb3KgxIpZV8t0082/kS3E=
|
||||||
github.com/therootcompany/golib/net/ipcohort v0.9.0/go.mod h1:Ljcnt2xOryVDGIVV5Kzt5hj2LhW4lBAWo5OeeVYU9ys=
|
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 h1:2WGFYzLhDyeGRuagLPe6Hp6gWMNqfqWpei9KG41C/9E=
|
||||||
github.com/therootcompany/golib/net/iplist v0.9.2/go.mod h1:TJ+0RP4M8/zkjdm+D9YXvDUJL2VZiC2VpnZJiK9Bfjk=
|
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 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
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) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ func (ps *IPPrefixSet) reload(ctx context.Context) (*ipcohort.Cohort, error) {
|
|||||||
paths[i] = ps.repo.FilePath(f)
|
paths[i] = ps.repo.FilePath(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cachedCohortValid(updated, ps.cohort.Load(), paths) {
|
if cachedCohortValid(status, ps.cohort.Load(), paths) {
|
||||||
return nil, nil
|
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)
|
return nil, fmt.Errorf("load files: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log().Info("prefix set loaded", "entries", commaify(cohort.Size()))
|
|
||||||
return cohort, nil
|
return cohort, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cachedCohortValid reports whether the current cohort can be reused
|
// cachedCohortValid reports whether the current cohort can be reused
|
||||||
// without reloading from disk: the repo was not updated, the cohort is
|
// without reloading from disk: the repo was not updated, the cohort is
|
||||||
// non-nil and non-empty, and all data files are present.
|
// non-nil and non-empty, and all data files are present.
|
||||||
func cachedCohortValid(updated bool, cohort *ipcohort.Cohort, paths []string) bool {
|
func cachedCohortValid(status gitshallow.UpdateStatus, cohort *ipcohort.Cohort, paths []string) bool {
|
||||||
return !updated && cohort != nil && cohort.Size() > 0 && filesPresent(paths)
|
return status != gitshallow.UpdateChanged && cohort != nil && cohort.Size() > 0 && filesPresent(paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filesPresent(paths []string) bool {
|
func filesPresent(paths []string) bool {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/therootcompany/golib/net/gitshallow"
|
||||||
"github.com/therootcompany/golib/net/ipcohort"
|
"github.com/therootcompany/golib/net/ipcohort"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,16 +33,16 @@ func TestCachedCohortValidRequiresFiles(t *testing.T) {
|
|||||||
t.Fatal(err)
|
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")
|
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")
|
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")
|
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")
|
t.Fatal("updated repo must reload")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user