@@ -1,8 +1,10 @@
5 "path/filepath"
6 "path/filepath"
7 "charm.land/lipgloss/v2"
9 "charm.land/lipgloss/v2"
8 "charm.land/lipgloss/v2/table"
10 "charm.land/lipgloss/v2/table"
@@ -17,16 +19,27 @@ var dirsCmd = &cobra.Command{
17 Long: `Print the directories where Crush stores its configuration and data files.
19 Long: `Print the directories where Crush stores its configuration and data files.
18This includes the global configuration directory and data directory.`,
20This includes the global configuration directory and data directory.`,
19 Example: `
21 Example: `
20−# Print all directories
22+# Print all global directories
25+# Print data directory and all project specific config directories
23−# Print only the config directory
28+# Print only global config directory
24crush dirs config
29crush dirs config
31+# Print only project specific config directories
26# Print only the data directory
34# Print only the data directory
27crush dirs data
35crush dirs data
29− Run: func(cmd *cobra.Command, args []string) {
37+ RunE: func(cmd *cobra.Command, args []string) error {
38+ dirs, err := configDirs(cmd)
40+ return fmt.Errorf("cannot collect config directories: %w", err)
30 if term.IsTerminal(os.Stdout.Fd()) {
43 if term.IsTerminal(os.Stdout.Fd()) {
31 // We're in a TTY: make it fancy.
44 // We're in a TTY: make it fancy.
32 t := table.New().
45 t := table.New().
@@ -34,22 +47,29 @@ crush dirs data
34 StyleFunc(func(row, col int) lipgloss.Style {
47 StyleFunc(func(row, col int) lipgloss.Style {
35 return lipgloss.NewStyle().Padding(0, 2)
48 return lipgloss.NewStyle().Padding(0, 2)
37− Row("Config", filepath.Dir(config.GlobalConfig())).
50+ Row("Config", dirs).
38 Row("Data", filepath.Dir(config.GlobalConfigData()))
51 Row("Data", filepath.Dir(config.GlobalConfigData()))
39 lipgloss.Println(t)
52 lipgloss.Println(t)
42 // Not a TTY.
55 // Not a TTY.
43− cmd.Println(filepath.Dir(config.GlobalConfig()))
56+ cmd.Println(dirs)
44 cmd.Println(filepath.Dir(config.GlobalConfigData()))
57 cmd.Println(filepath.Dir(config.GlobalConfigData()))
48var configDirCmd = &cobra.Command{
63var configDirCmd = &cobra.Command{
49 Use: "config",
64 Use: "config",
50 Short: "Print the configuration directory used by Crush",
65 Short: "Print the configuration directory used by Crush",
51− Run: func(cmd *cobra.Command, args []string) {
66+ RunE: func(cmd *cobra.Command, args []string) error {
52− cmd.Println(filepath.Dir(config.GlobalConfig()))
67+ dirs, err := configDirs(cmd)
69+ return fmt.Errorf("cannot collect config directories: %w", err)
@@ -61,6 +81,30 @@ var dataDirCmd = &cobra.Command{
84+// configDirs returns formatted string with one or more project config paths
85+func configDirs(cmd *cobra.Command) (string, error) {
86+ configDir := filepath.Dir(config.GlobalConfig())
87+ if ok, _ := cmd.Flags().GetBool("project"); !ok {
88+ return configDir, nil
91+ cwd, err := ResolveCwd(cmd)
93+ return "", fmt.Errorf("cannot resolve current working directory: %w", err)
96+ var sb strings.Builder
97+ for i, path := range config.ProjectConfigs(cwd) {
101+ sb.WriteString(filepath.Dir(path))
103+ return sb.String(), nil
64func init() {
106func init() {
107+ dirsCmd.PersistentFlags().BoolP("project", "p", false, "Print project specific configs")
65 dirsCmd.AddCommand(configDirCmd, dataDirCmd)
109 dirsCmd.AddCommand(configDirCmd, dataDirCmd)