on main help sign in
feat(cmd): support overriding the data directory

In some cases it might not be desirable to write `.crush` folders everywhere, or to reuse `.crush` folders between different filesystem locations. This change makes it possible to specify `-D` / `--data-dir` to set a different directory to load/create the project-specific data in.

commitf74abf2e75f88c405022cd3f74a058c91fa31583 authorVincent Ambo <mail@tazj.in> dateSat 9 Aug 2025, 00:50 (1 year ago) parent0c31481
6 files changed+54−40
internal/cmd/logs.go+6−1 internal/cmd/root.go+6−1 internal/config/init.go+2−2 internal/config/load.go+7−3 internal/config/load_test.go+32−32 internal/llm/provider/openai_test.go+1−1
internal/cmd/logs.go+6−1
@@ -28,6 +28,11 @@ var logsCmd = &cobra.Command{
2828 return fmt.Errorf("failed to get current working directory: %v", err)
2929 }
3030
31+ dataDir, err := cmd.Flags().GetString("data-dir")
32+ if err != nil {
33+ return fmt.Errorf("failed to get data directory: %v", err)
34+ }
35+
3136 follow, err := cmd.Flags().GetBool("follow")
3237 if err != nil {
3338 return fmt.Errorf("failed to get follow flag: %v", err)
@@ -41,7 +46,7 @@ var logsCmd = &cobra.Command{
4146 log.SetLevel(log.DebugLevel)
4247 log.SetOutput(os.Stdout)
4348
44− cfg, err := config.Load(cwd, false)
49+ cfg, err := config.Load(cwd, dataDir, false)
4550 if err != nil {
4651 return fmt.Errorf("failed to load configuration: %v", err)
4752 }
internal/cmd/root.go+6−1
@@ -21,6 +21,7 @@ import (
2121
2222func init() {
2323 rootCmd.PersistentFlags().StringP("cwd", "c", "", "Current working directory")
24+ rootCmd.PersistentFlags().StringP("data-dir", "D", "", "Custom crush data directory")
2425 rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug")
2526
2627 rootCmd.Flags().BoolP("help", "h", false, "Help")
@@ -45,6 +46,9 @@ crush -d
4546# Run with debug logging in a specific directory
4647crush -d -c /path/to/project
4748
49+# Run with custom data directory
50+crush -D /path/to/custom/.crush
51+
4852# Print version
4953crush -v
5054
@@ -96,6 +100,7 @@ func Execute() {
96100func setupApp(cmd *cobra.Command) (*app.App, error) {
97101 debug, _ := cmd.Flags().GetBool("debug")
98102 yolo, _ := cmd.Flags().GetBool("yolo")
103+ dataDir, _ := cmd.Flags().GetString("data-dir")
99104 ctx := cmd.Context()
100105
101106 cwd, err := ResolveCwd(cmd)
@@ -103,7 +108,7 @@ func setupApp(cmd *cobra.Command) (*app.App, error) {
103108 return nil, err
104109 }
105110
106− cfg, err := config.Init(cwd, debug)
111+ cfg, err := config.Init(cwd, dataDir, debug)
107112 if err != nil {
108113 return nil, err
109114 }
internal/config/init.go+2−2
@@ -19,8 +19,8 @@ type ProjectInitFlag struct {
1919// TODO: we need to remove the global config instance keeping it now just until everything is migrated
2020var instance atomic.Pointer[Config]
2121
22−func Init(workingDir string, debug bool) (*Config, error) {
23− cfg, err := Load(workingDir, debug)
22+func Init(workingDir, dataDir string, debug bool) (*Config, error) {
23+ cfg, err := Load(workingDir, dataDir, debug)
2424 if err != nil {
2525 return nil, err
2626 }
internal/config/load.go+7−3
@@ -37,7 +37,7 @@ func LoadReader(fd io.Reader) (*Config, error) {
3737}
3838
3939// Load loads the configuration from the default paths.
40−func Load(workingDir string, debug bool) (*Config, error) {
40+func Load(workingDir, dataDir string, debug bool) (*Config, error) {
4141 // uses default config paths
4242 configPaths := []string{
4343 globalConfig(),
@@ -52,7 +52,7 @@ func Load(workingDir string, debug bool) (*Config, error) {
5252
5353 cfg.dataConfigDir = GlobalConfigData()
5454
55− cfg.setDefaults(workingDir)
55+ cfg.setDefaults(workingDir, dataDir)
5656
5757 if debug {
5858 cfg.Options.Debug = true
@@ -299,7 +299,7 @@ func (c *Config) configureProviders(env env.Env, resolver VariableResolver, know
299299 return nil
300300}
301301
302−func (c *Config) setDefaults(workingDir string) {
302+func (c *Config) setDefaults(workingDir, dataDir string) {
303303 c.workingDir = workingDir
304304 if c.Options == nil {
305305 c.Options = &Options{}
@@ -317,6 +317,10 @@ func (c *Config) setDefaults(workingDir string) {
317317 c.Options.DataDirectory = filepath.Join(workingDir, defaultDataDirectory)
318318 }
319319 }
320+ // explicit dataDir flag always takes precedence
321+ if dataDir != "" {
322+ c.Options.DataDirectory = dataDir
323+ }
320324 if c.Providers == nil {
321325 c.Providers = csync.NewMap[string, ProviderConfig]()
322326 }
internal/config/load_test.go+32−32
@@ -39,7 +39,7 @@ func TestConfig_LoadFromReaders(t *testing.T) {
3939func TestConfig_setDefaults(t *testing.T) {
4040 cfg := &Config{}
4141
42− cfg.setDefaults("/tmp")
42+ cfg.setDefaults("/tmp", "")
4343
4444 require.NotNil(t, cfg.Options)
4545 require.NotNil(t, cfg.Options.TUI)
@@ -68,7 +68,7 @@ func TestConfig_configureProviders(t *testing.T) {
6868 }
6969
7070 cfg := &Config{}
71− cfg.setDefaults("/tmp")
71+ cfg.setDefaults("/tmp", "")
7272 env := env.NewFromMap(map[string]string{
7373 "OPENAI_API_KEY": "test-key",
7474 })
@@ -110,7 +110,7 @@ func TestConfig_configureProvidersWithOverride(t *testing.T) {
110110 },
111111 },
112112 })
113− cfg.setDefaults("/tmp")
113+ cfg.setDefaults("/tmp", "")
114114
115115 env := env.NewFromMap(map[string]string{
116116 "OPENAI_API_KEY": "test-key",
@@ -153,7 +153,7 @@ func TestConfig_configureProvidersWithNewProvider(t *testing.T) {
153153 },
154154 }),
155155 }
156− cfg.setDefaults("/tmp")
156+ cfg.setDefaults("/tmp", "")
157157 env := env.NewFromMap(map[string]string{
158158 "OPENAI_API_KEY": "test-key",
159159 })
@@ -188,7 +188,7 @@ func TestConfig_configureProvidersBedrockWithCredentials(t *testing.T) {
188188 }
189189
190190 cfg := &Config{}
191− cfg.setDefaults("/tmp")
191+ cfg.setDefaults("/tmp", "")
192192 env := env.NewFromMap(map[string]string{
193193 "AWS_ACCESS_KEY_ID": "test-key-id",
194194 "AWS_SECRET_ACCESS_KEY": "test-secret-key",
@@ -217,7 +217,7 @@ func TestConfig_configureProvidersBedrockWithoutCredentials(t *testing.T) {
217217 }
218218
219219 cfg := &Config{}
220− cfg.setDefaults("/tmp")
220+ cfg.setDefaults("/tmp", "")
221221 env := env.NewFromMap(map[string]string{})
222222 resolver := NewEnvironmentVariableResolver(env)
223223 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -239,7 +239,7 @@ func TestConfig_configureProvidersBedrockWithoutUnsupportedModel(t *testing.T) {
239239 }
240240
241241 cfg := &Config{}
242− cfg.setDefaults("/tmp")
242+ cfg.setDefaults("/tmp", "")
243243 env := env.NewFromMap(map[string]string{
244244 "AWS_ACCESS_KEY_ID": "test-key-id",
245245 "AWS_SECRET_ACCESS_KEY": "test-secret-key",
@@ -262,7 +262,7 @@ func TestConfig_configureProvidersVertexAIWithCredentials(t *testing.T) {
262262 }
263263
264264 cfg := &Config{}
265− cfg.setDefaults("/tmp")
265+ cfg.setDefaults("/tmp", "")
266266 env := env.NewFromMap(map[string]string{
267267 "VERTEXAI_PROJECT": "test-project",
268268 "VERTEXAI_LOCATION": "us-central1",
@@ -293,7 +293,7 @@ func TestConfig_configureProvidersVertexAIWithoutCredentials(t *testing.T) {
293293 }
294294
295295 cfg := &Config{}
296− cfg.setDefaults("/tmp")
296+ cfg.setDefaults("/tmp", "")
297297 env := env.NewFromMap(map[string]string{
298298 "GOOGLE_GENAI_USE_VERTEXAI": "false",
299299 "GOOGLE_CLOUD_PROJECT": "test-project",
@@ -319,7 +319,7 @@ func TestConfig_configureProvidersVertexAIMissingProject(t *testing.T) {
319319 }
320320
321321 cfg := &Config{}
322− cfg.setDefaults("/tmp")
322+ cfg.setDefaults("/tmp", "")
323323 env := env.NewFromMap(map[string]string{
324324 "GOOGLE_GENAI_USE_VERTEXAI": "true",
325325 "GOOGLE_CLOUD_LOCATION": "us-central1",
@@ -344,7 +344,7 @@ func TestConfig_configureProvidersSetProviderID(t *testing.T) {
344344 }
345345
346346 cfg := &Config{}
347− cfg.setDefaults("/tmp")
347+ cfg.setDefaults("/tmp", "")
348348 env := env.NewFromMap(map[string]string{
349349 "OPENAI_API_KEY": "test-key",
350350 })
@@ -472,7 +472,7 @@ func TestConfig_configureProvidersWithDisabledProvider(t *testing.T) {
472472 },
473473 }),
474474 }
475− cfg.setDefaults("/tmp")
475+ cfg.setDefaults("/tmp", "")
476476
477477 env := env.NewFromMap(map[string]string{
478478 "OPENAI_API_KEY": "test-key",
@@ -502,7 +502,7 @@ func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
502502 },
503503 }),
504504 }
505− cfg.setDefaults("/tmp")
505+ cfg.setDefaults("/tmp", "")
506506
507507 env := env.NewFromMap(map[string]string{})
508508 resolver := NewEnvironmentVariableResolver(env)
@@ -525,7 +525,7 @@ func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
525525 },
526526 }),
527527 }
528− cfg.setDefaults("/tmp")
528+ cfg.setDefaults("/tmp", "")
529529
530530 env := env.NewFromMap(map[string]string{})
531531 resolver := NewEnvironmentVariableResolver(env)
@@ -547,7 +547,7 @@ func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
547547 },
548548 }),
549549 }
550− cfg.setDefaults("/tmp")
550+ cfg.setDefaults("/tmp", "")
551551
552552 env := env.NewFromMap(map[string]string{})
553553 resolver := NewEnvironmentVariableResolver(env)
@@ -572,7 +572,7 @@ func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
572572 },
573573 }),
574574 }
575− cfg.setDefaults("/tmp")
575+ cfg.setDefaults("/tmp", "")
576576
577577 env := env.NewFromMap(map[string]string{})
578578 resolver := NewEnvironmentVariableResolver(env)
@@ -597,7 +597,7 @@ func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
597597 },
598598 }),
599599 }
600− cfg.setDefaults("/tmp")
600+ cfg.setDefaults("/tmp", "")
601601
602602 env := env.NewFromMap(map[string]string{})
603603 resolver := NewEnvironmentVariableResolver(env)
@@ -625,7 +625,7 @@ func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
625625 },
626626 }),
627627 }
628− cfg.setDefaults("/tmp")
628+ cfg.setDefaults("/tmp", "")
629629
630630 env := env.NewFromMap(map[string]string{})
631631 resolver := NewEnvironmentVariableResolver(env)
@@ -655,7 +655,7 @@ func TestConfig_configureProvidersCustomProviderValidation(t *testing.T) {
655655 },
656656 }),
657657 }
658− cfg.setDefaults("/tmp")
658+ cfg.setDefaults("/tmp", "")
659659
660660 env := env.NewFromMap(map[string]string{})
661661 resolver := NewEnvironmentVariableResolver(env)
@@ -688,7 +688,7 @@ func TestConfig_configureProvidersEnhancedCredentialValidation(t *testing.T) {
688688 },
689689 }),
690690 }
691− cfg.setDefaults("/tmp")
691+ cfg.setDefaults("/tmp", "")
692692
693693 env := env.NewFromMap(map[string]string{
694694 "GOOGLE_GENAI_USE_VERTEXAI": "false",
@@ -721,7 +721,7 @@ func TestConfig_configureProvidersEnhancedCredentialValidation(t *testing.T) {
721721 },
722722 }),
723723 }
724− cfg.setDefaults("/tmp")
724+ cfg.setDefaults("/tmp", "")
725725
726726 env := env.NewFromMap(map[string]string{})
727727 resolver := NewEnvironmentVariableResolver(env)
@@ -752,7 +752,7 @@ func TestConfig_configureProvidersEnhancedCredentialValidation(t *testing.T) {
752752 },
753753 }),
754754 }
755− cfg.setDefaults("/tmp")
755+ cfg.setDefaults("/tmp", "")
756756
757757 env := env.NewFromMap(map[string]string{})
758758 resolver := NewEnvironmentVariableResolver(env)
@@ -783,7 +783,7 @@ func TestConfig_configureProvidersEnhancedCredentialValidation(t *testing.T) {
783783 },
784784 }),
785785 }
786− cfg.setDefaults("/tmp")
786+ cfg.setDefaults("/tmp", "")
787787
788788 env := env.NewFromMap(map[string]string{
789789 "OPENAI_API_KEY": "test-key",
@@ -820,7 +820,7 @@ func TestConfig_defaultModelSelection(t *testing.T) {
820820 }
821821
822822 cfg := &Config{}
823− cfg.setDefaults("/tmp")
823+ cfg.setDefaults("/tmp", "")
824824 env := env.NewFromMap(map[string]string{})
825825 resolver := NewEnvironmentVariableResolver(env)
826826 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -856,7 +856,7 @@ func TestConfig_defaultModelSelection(t *testing.T) {
856856 }
857857
858858 cfg := &Config{}
859− cfg.setDefaults("/tmp")
859+ cfg.setDefaults("/tmp", "")
860860 env := env.NewFromMap(map[string]string{})
861861 resolver := NewEnvironmentVariableResolver(env)
862862 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -886,7 +886,7 @@ func TestConfig_defaultModelSelection(t *testing.T) {
886886 }
887887
888888 cfg := &Config{}
889− cfg.setDefaults("/tmp")
889+ cfg.setDefaults("/tmp", "")
890890 env := env.NewFromMap(map[string]string{})
891891 resolver := NewEnvironmentVariableResolver(env)
892892 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -929,7 +929,7 @@ func TestConfig_defaultModelSelection(t *testing.T) {
929929 },
930930 }),
931931 }
932− cfg.setDefaults("/tmp")
932+ cfg.setDefaults("/tmp", "")
933933 env := env.NewFromMap(map[string]string{})
934934 resolver := NewEnvironmentVariableResolver(env)
935935 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -973,7 +973,7 @@ func TestConfig_defaultModelSelection(t *testing.T) {
973973 },
974974 }),
975975 }
976− cfg.setDefaults("/tmp")
976+ cfg.setDefaults("/tmp", "")
977977 env := env.NewFromMap(map[string]string{})
978978 resolver := NewEnvironmentVariableResolver(env)
979979 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -1015,7 +1015,7 @@ func TestConfig_defaultModelSelection(t *testing.T) {
10151015 },
10161016 }),
10171017 }
1018− cfg.setDefaults("/tmp")
1018+ cfg.setDefaults("/tmp", "")
10191019 env := env.NewFromMap(map[string]string{})
10201020 resolver := NewEnvironmentVariableResolver(env)
10211021 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -1063,7 +1063,7 @@ func TestConfig_configureSelectedModels(t *testing.T) {
10631063 },
10641064 },
10651065 }
1066− cfg.setDefaults("/tmp")
1066+ cfg.setDefaults("/tmp", "")
10671067 env := env.NewFromMap(map[string]string{})
10681068 resolver := NewEnvironmentVariableResolver(env)
10691069 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -1125,7 +1125,7 @@ func TestConfig_configureSelectedModels(t *testing.T) {
11251125 },
11261126 },
11271127 }
1128− cfg.setDefaults("/tmp")
1128+ cfg.setDefaults("/tmp", "")
11291129 env := env.NewFromMap(map[string]string{})
11301130 resolver := NewEnvironmentVariableResolver(env)
11311131 err := cfg.configureProviders(env, resolver, knownProviders)
@@ -1170,7 +1170,7 @@ func TestConfig_configureSelectedModels(t *testing.T) {
11701170 },
11711171 },
11721172 }
1173− cfg.setDefaults("/tmp")
1173+ cfg.setDefaults("/tmp", "")
11741174 env := env.NewFromMap(map[string]string{})
11751175 resolver := NewEnvironmentVariableResolver(env)
11761176 err := cfg.configureProviders(env, resolver, knownProviders)
internal/llm/provider/openai_test.go+1−1
@@ -17,7 +17,7 @@ import (
1717)
1818
1919func TestMain(m *testing.M) {
20− _, err := config.Init(".", true)
20+ _, err := config.Init(".", "", true)
2121 if err != nil {
2222 panic("Failed to initialize config: " + err.Error())
2323 }