From 68ec5cb659dc2a4d4e972e891c41c84e8736111b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 06:18:45 +0000 Subject: [PATCH] detect: cache Default() result with sync.Once, add Reset() Co-authored-by: taigrr <8261498+taigrr@users.noreply.github.com> --- detect/detect.go | 34 ++++++++++++++++++++++++++++------ detect/detect_test.go | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/detect/detect.go b/detect/detect.go index 0d9736d..58e7fed 100644 --- a/detect/detect.go +++ b/detect/detect.go @@ -3,20 +3,42 @@ package detect import ( "os/exec" + "sync" "github.com/gogrlx/snack" ) +var ( + defaultOnce sync.Once + defaultMgr snack.Manager + defaultErr error +) + // Default returns the first available package manager on the system. +// The result is cached after the first call. // Returns ErrManagerNotFound if no supported manager is detected. func Default() (snack.Manager, error) { - for _, fn := range candidates() { - m := fn() - if m.Available() { - return m, nil + defaultOnce.Do(func() { + for _, fn := range candidates() { + m := fn() + if m.Available() { + defaultMgr = m + return + } } - } - return nil, snack.ErrManagerNotFound + defaultErr = snack.ErrManagerNotFound + }) + return defaultMgr, defaultErr +} + +// Reset clears the cached result of Default(), forcing re-detection on the +// next call. This is intended for use in tests or dynamic environments where +// the available package managers may change. +// Reset is not safe to call concurrently with Default(). +func Reset() { + defaultOnce = sync.Once{} + defaultMgr = nil + defaultErr = nil } // All returns all available package managers on the system. diff --git a/detect/detect_test.go b/detect/detect_test.go index fa7242a..fd3ba5f 100644 --- a/detect/detect_test.go +++ b/detect/detect_test.go @@ -20,3 +20,26 @@ func TestDefaultDoesNotPanic(t *testing.T) { // May return error if no managers available; that's fine. _, _ = Default() } + +func TestDefaultCachesResult(t *testing.T) { + Reset() + m1, err1 := Default() + m2, err2 := Default() + if m1 != m2 { + t.Error("expected Default() to return the same Manager on repeated calls") + } + if err1 != err2 { + t.Error("expected Default() to return the same error on repeated calls") + } + // Exactly one of m1/err1 must be non-nil (either a manager was found or not). + if (m1 == nil) == (err1 == nil) { + t.Error("expected exactly one of Manager or error to be non-nil") + } +} + +func TestResetAllowsRedetection(t *testing.T) { + _, _ = Default() + Reset() + // After reset, defaultOnce should be fresh; calling Default() again should work. + _, _ = Default() +}