on main help sign in
feat: add `crush update-providers` command
c0edda6Andrey Nering
internal/cmd/root.go+1
@@ -28,6 +28,7 @@ func init() {
28 rootCmd.Flags().BoolP("yolo", "y", false, "Automatically accept all permissions (dangerous mode)")
28 rootCmd.Flags().BoolP("yolo", "y", false, "Automatically accept all permissions (dangerous mode)")
29
29
30 rootCmd.AddCommand(runCmd)
30 rootCmd.AddCommand(runCmd)
31+ rootCmd.AddCommand(updateProvidersCmd)
31}
32}
32
33
33var rootCmd = &cobra.Command{
34var rootCmd = &cobra.Command{
internal/cmd/update_providers.gonew file+60
@@ -0,0 +1,60 @@
1+package cmd
2+
3+import (
4+ "fmt"
5+ "log/slog"
6+
7+ "github.com/charmbracelet/crush/internal/config"
8+ "github.com/charmbracelet/lipgloss/v2"
9+ "github.com/charmbracelet/x/exp/charmtone"
10+ "github.com/spf13/cobra"
11+)
12+
13+var updateProvidersCmd = &cobra.Command{
14+ Use: "update-providers [path-or-url]",
15+ Short: "Update providers",
16+ Long: `Update the list of providers from a specified local path or remote URL.`,
17+ Example: `
18+# Update providers remotely from Catwalk
19+crush update-providers
20+
21+# Update providers from a custom URL
22+crush update-providers https://example.com/
23+
24+# Update providers from a local file
25+crush update-providers /path/to/local-providers.json
26+
27+# Update providers from embedded version
28+crush update-providers embedded
29+`,
30+ RunE: func(cmd *cobra.Command, args []string) error {
31+ // NOTE(@andreynering): We want to skip logging output do stdout here.
32+ slog.SetDefault(slog.New(slog.DiscardHandler))
33+
34+ var pathOrUrl string
35+ if len(args) > 0 {
36+ pathOrUrl = args[0]
37+ }
38+
39+ if err := config.UpdateProviders(pathOrUrl); err != nil {
40+ return err
41+ }
42+
43+ // NOTE(@andreynering): This style is more-or-less copied from Fang's
44+ // error message, adapted for success.
45+ headerStyle := lipgloss.NewStyle().
46+ Foreground(charmtone.Butter).
47+ Background(charmtone.Guac).
48+ Bold(true).
49+ Padding(0, 1).
50+ Margin(1).
51+ MarginLeft(2).
52+ SetString("SUCCESS")
53+ textStyle := lipgloss.NewStyle().
54+ MarginLeft(2).
55+ SetString("Providers updated successfully.")
56+
57+ fmt.Printf("%s\n%s\n\n", headerStyle.Render(), textStyle.Render())
58+ return nil
59+ },
60+}
internal/config/provider.go+36
@@ -8,6 +8,7 @@ import (
8 "os"
8 "os"
9 "path/filepath"
9 "path/filepath"
10 "runtime"
10 "runtime"
11+ "strings"
11 "sync"
12 "sync"
12 "time"
13 "time"
13
14
@@ -77,6 +78,41 @@ func loadProvidersFromCache(path string) ([]catwalk.Provider, error) {
77 return providers, nil
78 return providers, nil
78}
79}
79
80
81+func UpdateProviders(pathOrUrl string) error {
82+ var providers []catwalk.Provider
83+ pathOrUrl = cmp.Or(pathOrUrl, os.Getenv("CATWALK_URL"), defaultCatwalkURL)
84+
85+ switch {
86+ case pathOrUrl == "embedded":
87+ providers = embedded.GetAll()
88+ case strings.HasPrefix(pathOrUrl, "http://") || strings.HasPrefix(pathOrUrl, "https://"):
89+ var err error
90+ providers, err = catwalk.NewWithURL(pathOrUrl).GetProviders()
91+ if err != nil {
92+ return fmt.Errorf("failed to fetch providers from Catwalk: %w", err)
93+ }
94+ default:
95+ content, err := os.ReadFile(pathOrUrl)
96+ if err != nil {
97+ return fmt.Errorf("failed to read file: %w", err)
98+ }
99+ if err := json.Unmarshal(content, &providers); err != nil {
100+ return fmt.Errorf("failed to unmarshal provider data: %w", err)
101+ }
102+ if len(providers) == 0 {
103+ return fmt.Errorf("no providers found in the provided source")
104+ }
105+ }
106+
107+ cachePath := providerCacheFileData()
108+ if err := saveProvidersInCache(cachePath, providers); err != nil {
109+ return fmt.Errorf("failed to save providers to cache: %w", err)
110+ }
111+
112+ slog.Info("Providers updated successfully", "count", len(providers), "from", pathOrUrl, "to", cachePath)
113+ return nil
114+}
115+
80func Providers(cfg *Config) ([]catwalk.Provider, error) {
116func Providers(cfg *Config) ([]catwalk.Provider, error) {
81 providerOnce.Do(func() {
117 providerOnce.Do(func() {
82 catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)
118 catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)