Skip to content

Commit 2454542

Browse files
authored
Merge pull request #309 from numtide/find-tree-root
cli: search for the tree root by default
2 parents 3f44ec4 + ed2960d commit 2454542

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

cli/cli.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ type Format struct {
1313
WorkingDirectory kong.ChangeDirFlag `default:"." short:"C" help:"Run as if treefmt was started in the specified working directory instead of the current working directory."`
1414
NoCache bool `help:"Ignore the evaluation cache entirely. Useful for CI."`
1515
ClearCache bool `short:"c" help:"Reset the evaluation cache. Use in case the cache is not precise enough."`
16-
ConfigFile string `type:"existingfile" default:"./treefmt.toml" help:"The config file to use."`
16+
ConfigFile string `type:"existingfile" help:"Load the config file from the given path (defaults to searching upwards for treefmt.toml)."`
1717
FailOnChange bool `help:"Exit with error if any changes were made. Useful for CI."`
1818
Formatters []string `short:"f" help:"Specify formatters to apply. Defaults to all formatters."`
19-
TreeRoot string `type:"existingdir" default:"." help:"The root directory from which treefmt will start walking the filesystem."`
19+
TreeRoot string `type:"existingdir" xor:"tree-root" help:"The root directory from which treefmt will start walking the filesystem (defaults to the directory containing the config file)."`
20+
TreeRootFile string `type:"string" xor:"tree-root" help:"File to search for to find the project root (if --tree-root is not passed)."`
2021
Walk walk.Type `enum:"auto,git,filesystem" default:"auto" help:"The method used to traverse the files within --tree-root. Currently supports 'auto', 'git' or 'filesystem'."`
2122
Verbosity int `name:"verbose" short:"v" type:"counter" default:"0" env:"LOG_LEVEL" help:"Set the verbosity of logs e.g. -vv."`
2223
Version bool `name:"version" short:"V" help:"Print version."`

cli/format.go

+84
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,37 @@ func (f *Format) Run() (err error) {
6969
}
7070
}()
7171

72+
// find the config file unless specified
73+
if Cli.ConfigFile == "" {
74+
pwd, err := os.Getwd()
75+
if err != nil {
76+
return err
77+
}
78+
Cli.ConfigFile, _, err = findUp(pwd, "treefmt.toml")
79+
if err != nil {
80+
return err
81+
}
82+
}
83+
84+
// default the tree root to the directory containing the config file
85+
if Cli.TreeRoot == "" {
86+
Cli.TreeRoot = filepath.Dir(Cli.ConfigFile)
87+
}
88+
89+
// search the tree root using the --tree-root-file if specified
90+
if Cli.TreeRootFile != "" {
91+
pwd, err := os.Getwd()
92+
if err != nil {
93+
return err
94+
}
95+
_, Cli.TreeRoot, err = findUp(pwd, Cli.TreeRootFile)
96+
if err != nil {
97+
return err
98+
}
99+
}
100+
101+
log.Debugf("config-file=%s tree-root=%s", Cli.ConfigFile, Cli.TreeRoot)
102+
72103
// read config
73104
cfg, err := config.ReadFile(Cli.ConfigFile, Cli.Formatters)
74105
if err != nil {
@@ -384,3 +415,56 @@ func applyFormatters(ctx context.Context) func() error {
384415
return nil
385416
}
386417
}
418+
419+
func findUp(searchDir string, fileName string) (path string, dir string, err error) {
420+
for _, dir := range eachDir(searchDir) {
421+
path := filepath.Join(dir, fileName)
422+
if fileExists(path) {
423+
return path, dir, nil
424+
}
425+
}
426+
return "", "", fmt.Errorf("could not find %s in %s", fileName, searchDir)
427+
}
428+
429+
func eachDir(path string) (paths []string) {
430+
path, err := filepath.Abs(path)
431+
if err != nil {
432+
return
433+
}
434+
435+
paths = []string{path}
436+
437+
if path == "/" {
438+
return
439+
}
440+
441+
for i := len(path) - 1; i >= 0; i-- {
442+
if path[i] == os.PathSeparator {
443+
path = path[:i]
444+
if path == "" {
445+
path = "/"
446+
}
447+
paths = append(paths, path)
448+
}
449+
}
450+
451+
return
452+
}
453+
454+
func fileExists(path string) bool {
455+
// Some broken filesystems like SSHFS return file information on stat() but
456+
// then cannot open the file. So we use os.Open.
457+
f, err := os.Open(path)
458+
if err != nil {
459+
return false
460+
}
461+
defer f.Close()
462+
463+
// Next, check that the file is a regular file.
464+
fi, err := f.Stat()
465+
if err != nil {
466+
return false
467+
}
468+
469+
return fi.Mode().IsRegular()
470+
}

0 commit comments

Comments
 (0)