package main import ( "fmt" "os/exec" "log" "io" "os" "time" "slices" "regexp" "io/fs" "path/filepath" "errors" "lcthw.dev/go/ttarpit/config" "github.com/fsnotify/fsnotify" ) func LaunchLogger(in io.Reader, out io.Writer, err error) { if err != nil { log.Fatal(err) } go func() { if _, err := io.Copy(out, in) err != nil { log.Printf("LOGGER: %v", err) } }() } func LaunchProcess(proc *config.Process) { proc.ExecCmd = exec.Command(proc.Command, proc.Args...) if errors.Is(proc.ExecCmd.Err, exec.ErrDot) { proc.ExecCmd.Err = nil } fmt.Println("STARTING", proc.Command) stderr, err := proc.ExecCmd.StderrPipe(); LaunchLogger(stderr, os.Stdout, err) stdout, err := proc.ExecCmd.StdoutPipe(); LaunchLogger(stdout, os.Stdout, err) err = proc.ExecCmd.Start() if err != nil { log.Fatalf("FAIL %s %s err=%v", proc.Command, proc.Args, err) } fmt.Println("WAITING for", proc.Command) fmt.Println("SENDING READY on channel") fmt.Println("SENT READY") proc.ExecCmd.Wait() fmt.Println("PROCESS", proc.Command, "EXITED") } func MatchesPath(fp string) bool { fp = filepath.ToSlash(fp) is_included := slices.ContainsFunc(config.Settings.Includes, func (reg string) bool { matches, err := regexp.MatchString(reg, fp) if err != nil { log.Panic("invalid excludes regex", reg, "error: ", err) } return matches }) return is_included } func RunBuild() { if !config.Settings.BuildRunning { config.Settings.BuildRunning = true defer func(){config.Settings.BuildRunning = false}() fmt.Println("CONFIG:", config.Settings.ConfigPath) fmt.Println("COMMANDs:", config.Settings.Processes) for name, proc := range config.Settings.Processes { fmt.Println("PROCESS:", name) LaunchProcess(&proc) fmt.Println("=================== PROCESS EXIT") } time.Sleep(1000 * time.Millisecond) } else { fmt.Println("!!!! BUILD SKIP, already running") } } func AddWatchDir(watcher *fsnotify.Watcher, name string) error { return filepath.WalkDir(name, func(path string, d fs.DirEntry, err error) error { if err != nil { log.Printf("WATCH ERROR! walking=%s path=%s: err=%v", name, path, err) return err } if d.IsDir() { log.Println("WATCHING: ", path) err = watcher.Add(path) if err != nil { log.Printf("failed to watch %s", path) return err } } return nil }) } func WatchDir() { watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal("Failed to start fsnotify", err) } defer watcher.Close() go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } if event.Has(fsnotify.Create) { log.Println("---> CREATE", event.Name) AddWatchDir(watcher, event.Name) } else if event.Has(fsnotify.Write) { // check if match then do thing if MatchesPath(event.Name) { go RunBuild() } } else { log.Println("event:", event) } case err, ok := <-watcher.Errors: if !ok { return } log.Println("error: ", err) } } }() err = AddWatchDir(watcher, ".") if err != nil { log.Fatal(err, "Failed to watch .") } <-make(chan struct{}) } func main() { config.Load() fmt.Println("WAITING....") WatchDir() }