@@ -12,6 +12,7 @@ import (
14 "github.com/charmbracelet/catwalk/pkg/catwalk"
14 "github.com/charmbracelet/catwalk/pkg/catwalk"
15+ "github.com/charmbracelet/catwalk/pkg/embedded"
15 "github.com/charmbracelet/crush/internal/home"
16 "github.com/charmbracelet/crush/internal/home"
@@ -22,6 +23,7 @@ type ProviderClient interface {
23 providerOnce sync.Once
24 providerOnce sync.Once
24 providerList []catwalk.Provider
25 providerList []catwalk.Provider
27// file to cache provider data
29// file to cache provider data
@@ -75,55 +77,93 @@ func loadProvidersFromCache(path string) ([]catwalk.Provider, error) {
75 return providers, nil
77 return providers, nil
78−func Providers() ([]catwalk.Provider, error) {
80+func Providers(cfg *Config) ([]catwalk.Provider, error) {
79− catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)
80− client := catwalk.NewWithURL(catwalkURL)
81− path := providerCacheFileData()
82− return loadProvidersOnce(client, path)
85−func loadProvidersOnce(client ProviderClient, path string) ([]catwalk.Provider, error) {
87 providerOnce.Do(func() {
81 providerOnce.Do(func() {
82+ catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)
83+ client := catwalk.NewWithURL(catwalkURL)
84+ path := providerCacheFileData()
86+ autoUpdateDisabled := cfg.Options.DisableProviderAutoUpdate
88− providerList, err = loadProviders(client, path)
87+ providerList, providerErr = loadProviders(autoUpdateDisabled, client, path)
93− return providerList, nil
89+ return providerList, providerErr
96−func loadProviders(client ProviderClient, path string) (providerList []catwalk.Provider, err error) {
92+func loadProviders(autoUpdateDisabled bool, client ProviderClient, path string) ([]catwalk.Provider, error) {
97− // if cache is not stale, load from it
98− stale, exists := isCacheStale(path)
93+ cacheIsStale, cacheExists := isCacheStale(path)
100− slog.Info("Using cached provider data", "path", path)
95+ catwalkGetAndSave := func() ([]catwalk.Provider, error) {
101− providerList, err = loadProvidersFromCache(path)
102− if len(providerList) > 0 && err == nil {
104− slog.Info("Updating provider cache in background", "path", path)
105− updated, uerr := client.GetProviders()
96+ providers, err := client.GetProviders()
106− if len(updated) > 0 && uerr == nil {
97+ if err != nil {
98+ return nil, fmt.Errorf("failed to fetch providers from catwalk: %w", err)
100+ if len(providers) == 0 {
101+ return nil, fmt.Errorf("empty providers list from catwalk")
107− _ = saveProvidersInCache(path, updated)
103+ if err := saveProvidersInCache(path, providers); err != nil {
110− return
104+ return nil, err
106+ return providers, nil
114− slog.Info("Getting live provider data", "path", path)
109+ backgroundCacheUpdate := func() {
111+ slog.Info("Updating providers cache in background", "path", path)
115− providerList, err = client.GetProviders()
113+ providers, err := client.GetProviders()
116− if len(providerList) > 0 && err == nil {
114+ if err != nil {
115+ slog.Error("Failed to fetch providers in background from Catwalk", "error", err)
118+ if len(providers) == 0 {
119+ slog.Error("Empty providers list from Catwalk")
117− err = saveProvidersInCache(path, providerList)
122+ if err := saveProvidersInCache(path, providers); err != nil {
118− return
123+ slog.Error("Failed to update providers.json in background", "error", err)
121− err = fmt.Errorf("failed to load providers")
128+ switch {
129+ case autoUpdateDisabled:
130+ slog.Warn("Providers auto-update is disabled")
133+ slog.Warn("Using locally cached providers")
134+ return loadProvidersFromCache(path)
137+ slog.Warn("Saving embedded providers to cache")
138+ providers := embedded.GetAll()
139+ if err := saveProvidersInCache(path, providers); err != nil {
142+ return providers, nil
144+ case cacheExists && !cacheIsStale:
145+ slog.Info("Recent providers cache is available.", "path", path)
147+ providers, err := loadProvidersFromCache(path)
151+ if len(providers) == 0 {
152+ return catwalkGetAndSave()
154+ backgroundCacheUpdate()
155+ return providers, nil
158+ slog.Info("Cache is not available or is stale. Fetching providers from Catwalk.", "path", path)
160+ providers, err := catwalkGetAndSave()
162+ catwalkUrl := fmt.Sprintf("%s/providers", cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL))
163+ return nil, fmt.Errorf("crush was unable to fetch an updated list of providers from %s. Consider setting CRUSH_DISABLE_PROVIDER_AUTO_UPDATE=1 to use embedded version from the time of this Crush release. %w", catwalkUrl, err)
122− return
165+ return providers, nil
124− slog.Info("Loading provider data from cache", "path", path)
125− providerList, err = loadProvidersFromCache(path)
129func isCacheStale(path string) (stale, exists bool) {
169func isCacheStale(path string) (stale, exists bool) {