Files
diachron/master/runexpress.go
2026-01-01 13:24:36 -06:00

125 lines
2.4 KiB
Go

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()
})
}
}