14 "github.com/charmbracelet/catwalk/pkg/catwalk"
15 "github.com/charmbracelet/catwalk/pkg/embedded"
16 "github.com/charmbracelet/crush/internal/home"
19type ProviderClient interface {
20 GetProviders() ([]catwalk.Provider, error)
24 providerOnce sync.Once
25 providerList []catwalk.Provider
29// file to cache provider data
30func providerCacheFileData() string {
31 xdgDataHome := os.Getenv("XDG_DATA_HOME")
32 if xdgDataHome != "" {
33 return filepath.Join(xdgDataHome, appName, "providers.json")
36 // return the path to the main data directory
37 // for windows, it should be in `%LOCALAPPDATA%/crush/`
38 // for linux and macOS, it should be in `$HOME/.local/share/crush/`
39 if runtime.GOOS == "windows" {
40 localAppData := os.Getenv("LOCALAPPDATA")
41 if localAppData == "" {
42 localAppData = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local")
44 return filepath.Join(localAppData, appName, "providers.json")
47 return filepath.Join(home.Dir(), ".local", "share", appName, "providers.json")
50func saveProvidersInCache(path string, providers []catwalk.Provider) error {
51 slog.Info("Saving provider data to disk", "path", path)
52 if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
53 return fmt.Errorf("failed to create directory for provider cache: %w", err)
56 data, err := json.MarshalIndent(providers, "", " ")
58 return fmt.Errorf("failed to marshal provider data: %w", err)
61 if err := os.WriteFile(path, data, 0o644); err != nil {
62 return fmt.Errorf("failed to write provider data to cache: %w", err)
67func loadProvidersFromCache(path string) ([]catwalk.Provider, error) {
68 data, err := os.ReadFile(path)
70 return nil, fmt.Errorf("failed to read provider cache file: %w", err)
73 var providers []catwalk.Provider
74 if err := json.Unmarshal(data, &providers); err != nil {
75 return nil, fmt.Errorf("failed to unmarshal provider data from cache: %w", err)
80func UpdateProviders(pathOrUrl string) error {
81 var providers []catwalk.Provider
82 pathOrUrl = cmp.Or(pathOrUrl, os.Getenv("CATWALK_URL"), defaultCatwalkURL)
85 case pathOrUrl == "embedded":
86 providers = embedded.GetAll()
87 case strings.HasPrefix(pathOrUrl, "http://") || strings.HasPrefix(pathOrUrl, "https://"):
89 providers, err = catwalk.NewWithURL(pathOrUrl).GetProviders()
91 return fmt.Errorf("failed to fetch providers from Catwalk: %w", err)
94 content, err := os.ReadFile(pathOrUrl)
96 return fmt.Errorf("failed to read file: %w", err)
98 if err := json.Unmarshal(content, &providers); err != nil {
99 return fmt.Errorf("failed to unmarshal provider data: %w", err)
101 if len(providers) == 0 {
102 return fmt.Errorf("no providers found in the provided source")
106 cachePath := providerCacheFileData()
107 if err := saveProvidersInCache(cachePath, providers); err != nil {
108 return fmt.Errorf("failed to save providers to cache: %w", err)
111 slog.Info("Providers updated successfully", "count", len(providers), "from", pathOrUrl, "to", cachePath)
115func Providers(cfg *Config) ([]catwalk.Provider, error) {
116 providerOnce.Do(func() {
117 catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)
118 client := catwalk.NewWithURL(catwalkURL)
119 path := providerCacheFileData()
121 autoUpdateDisabled := cfg.Options.DisableProviderAutoUpdate
122 providerList, providerErr = loadProviders(autoUpdateDisabled, client, path)
124 return providerList, providerErr
127func loadProviders(autoUpdateDisabled bool, client ProviderClient, path string) ([]catwalk.Provider, error) {
128 catwalkGetAndSave := func() ([]catwalk.Provider, error) {
129 providers, err := client.GetProviders()
131 return nil, fmt.Errorf("failed to fetch providers from catwalk: %w", err)
133 if len(providers) == 0 {
134 return nil, fmt.Errorf("empty providers list from catwalk")
136 if err := saveProvidersInCache(path, providers); err != nil {
139 return providers, nil
143 case autoUpdateDisabled:
144 slog.Warn("Providers auto-update is disabled")
146 if _, err := os.Stat(path); err == nil {
147 slog.Warn("Using locally cached providers")
148 return loadProvidersFromCache(path)
151 slog.Warn("Saving embedded providers to cache")
152 providers := embedded.GetAll()
153 if err := saveProvidersInCache(path, providers); err != nil {
156 return providers, nil
159 slog.Info("Fetching providers from Catwalk.", "path", path)
161 providers, err := catwalkGetAndSave()
163 catwalkUrl := fmt.Sprintf("%s/v2/providers", cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL))
164 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 the embedded providers bundled at the time of this Crush release. You can also update providers manually. For more info see crush update-providers --help. %w", catwalkUrl, err) //nolint:staticcheck
166 return providers, nil
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list