diff --git a/master/main.go b/master/main.go index 5c09175..2b011da 100644 --- a/master/main.go +++ b/master/main.go @@ -27,7 +27,7 @@ func main() { go watchFiles(watchedDir, fileChanges) - go printChanges(fileChanges) + go runExpress(fileChanges) // WaitGroup to track both processes // var wg sync.WaitGroup diff --git a/master/printchanges.go b/master/printchanges.go deleted file mode 100644 index dcb0ad9..0000000 --- a/master/printchanges.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "fmt" -) - -func printChanges(changes <-chan FileChange) { - for change := range changes { - fmt.Printf("[%s] %s\n", change.Operation, change.Path) - } -} diff --git a/master/runexpress.go b/master/runexpress.go new file mode 100644 index 0000000..9b2e2c4 --- /dev/null +++ b/master/runexpress.go @@ -0,0 +1,124 @@ +package main + +import ( + "io" + "log" + "os" + "os/exec" + "sync" + "syscall" + "time" +) + +func runExpress(changes <-chan FileChange) { + var currentProcess *exec.Cmd + var mu sync.Mutex + + // Helper to start the express process + startExpress := func() *exec.Cmd { + cmd := exec.Command("node", "../express/dist/index.js") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Start(); err != nil { + log.Printf("[express] Failed to start: %v", err) + return nil + } + + log.Printf("[express] Started (pid %d)", cmd.Process.Pid) + + // Monitor the process in background + go func() { + err := cmd.Wait() + if err != nil { + log.Printf("[express] Process exited: %v", err) + } else { + log.Printf("[express] Process exited normally") + } + }() + + return cmd + } + + // Helper to stop the express process + stopExpress := func(cmd *exec.Cmd) { + if cmd == nil || cmd.Process == nil { + return + } + + log.Printf("[express] Stopping (pid %d)", cmd.Process.Pid) + cmd.Process.Signal(syscall.SIGTERM) + + // Wait briefly for graceful shutdown + done := make(chan struct{}) + go func() { + cmd.Wait() + close(done) + }() + + select { + case <-done: + log.Printf("[express] Stopped gracefully") + case <-time.After(5 * time.Second): + log.Printf("[express] Force killing") + cmd.Process.Kill() + } + } + + // Helper to run the build + runBuild := func() bool { + log.Printf("[build] Starting ncc build...") + + cmd := exec.Command("sh", "-c", "cd ../express && ../cmd pnpm ncc build ./app.ts") + + stdout, _ := cmd.StdoutPipe() + stderr, _ := cmd.StderrPipe() + + if err := cmd.Start(); err != nil { + log.Printf("[build] Failed to start: %v", err) + return false + } + + // Copy output + go io.Copy(os.Stdout, stdout) + go io.Copy(os.Stderr, stderr) + + err := cmd.Wait() + if err != nil { + log.Printf("[build] Failed: %v", err) + return false + } + + log.Printf("[build] Success") + return true + } + + // Debounce timer + var debounceTimer *time.Timer + const debounceDelay = 100 * time.Millisecond + + for change := range changes { + log.Printf("[watch] %s: %s", change.Operation, change.Path) + + // Reset debounce timer + if debounceTimer != nil { + debounceTimer.Stop() + } + + debounceTimer = time.AfterFunc(debounceDelay, func() { + if !runBuild() { + log.Printf("[master] Build failed, keeping current process") + return + } + + mu.Lock() + defer mu.Unlock() + + // Stop old process + stopExpress(currentProcess) + + // Start new process + currentProcess = startExpress() + }) + } +}