66 "fmt"
77 "log"
88 "os"
9+ "path/filepath"
910 "strings"
1011 "time"
1112
@@ -14,6 +15,17 @@ import (
1415 "gopkg.in/yaml.v3"
1516)
1617
18+ const (
19+ discoverMaxDepth = 7
20+ )
21+
22+ var (
23+ discoverNames = []string {
24+ ".fexec.yaml" ,
25+ ".fexec.yml" ,
26+ }
27+ )
28+
1729type CommandConfig struct {
1830 Commands map [string ]framework.CommandModule [any ] `yaml:"commands"`
1931}
@@ -45,14 +57,25 @@ func (cfg *CommandConfig) PrintUsage() {
4557func main () {
4658 fs := flag .NewFlagSet ("fexec" , flag .ContinueOnError )
4759
48- configPath := fs .String ("c" , ".fexec.yaml" , "Path to config file" )
60+ wd := "."
61+ configPath := fs .String ("c" , "" , "Path to config file" )
4962
5063 flagErr := fs .Parse (os .Args [1 :])
5164 printUsage := errors .Is (flagErr , flag .ErrHelp ) || len (os .Args ) == 1
5265 if ! printUsage && flagErr != nil {
5366 log .Fatalf ("parsing options: %s" , flagErr )
5467 }
5568
69+ if * configPath == "" {
70+ var err error
71+ * configPath , err = DiscoverConfigPath ()
72+ if err != nil {
73+ log .Fatalf ("discovering config path: %s" , err )
74+ }
75+
76+ wd = filepath .Dir (* configPath )
77+ }
78+
5679 cfg , err := ParseConfig (* configPath )
5780 if err != nil {
5881 log .Fatalf ("reading config: %s" , err )
@@ -78,6 +101,12 @@ func main() {
78101 continue
79102 }
80103
104+ if module .Dir == "" {
105+ module .Dir = wd
106+ } else if ! filepath .IsAbs (module .Dir ) {
107+ module .Dir = filepath .Join (wd , module .Dir )
108+ }
109+
81110 modules [name ] = & framework.CommandModule [any ]{
82111 Command : module .Command ,
83112 Dir : module .Dir ,
@@ -113,3 +142,26 @@ func SetupCommonEnv() error {
113142 }
114143 return nil
115144}
145+
146+ func DiscoverConfigPath () (string , error ) {
147+ wd , err := os .Getwd ()
148+ if err != nil {
149+ return "" , fmt .Errorf ("getwd: %w" , err )
150+ }
151+
152+ for depth := 0 ; depth < discoverMaxDepth ; depth ++ {
153+ for _ , name := range discoverNames {
154+ path := fmt .Sprintf ("%s/%s" , wd , name )
155+
156+ if _ , err = os .Stat (path ); err == nil {
157+ return path , nil
158+ }
159+ if ! errors .Is (err , os .ErrNotExist ) {
160+ return "" , fmt .Errorf ("stat %q: %w" , path , err )
161+ }
162+ }
163+ wd = filepath .Dir (wd )
164+ }
165+
166+ return "" , fmt .Errorf ("file not found (searched for %s)" , strings .Join (discoverNames , ", " ))
167+ }
0 commit comments