package main import ( "flag" "fmt" "log" "os" "os/signal" "path/filepath" "syscall" "time" "Moonshark/modules/http" "Moonshark/modules/sql" "Moonshark/state" ) var ( watchFlag = flag.Bool("watch", false, "Watch script files for changes and restart") wFlag = flag.Bool("w", false, "Watch script files for changes and restart") ) func main() { flag.Parse() if flag.NArg() < 1 { fmt.Fprintf(os.Stderr, "Usage: %s [--watch|-w] \n", filepath.Base(os.Args[0])) os.Exit(1) } scriptPath := flag.Arg(0) watchMode := *watchFlag || *wFlag if watchMode { runWithWatcher(scriptPath) } else { runOnce(scriptPath) } } func runOnce(scriptPath string) { luaState, err := state.NewFromScript(scriptPath) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } defer luaState.Close() if err := luaState.ExecuteFile(scriptPath); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } if http.HasActiveServers() { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) fmt.Println("HTTP servers running. Press Ctrl+C to exit.") go func() { <-sigChan fmt.Println("\nShutting down...") // Close main state first (saves KV stores) luaState.Close() // Then stop servers (closes worker states) http.StopAllServers() sql.CloseAllConnections() os.Exit(0) }() http.WaitForServers() } } func runWithWatcher(scriptPath string) { watcher, err := NewFileWatcher(500) // 500ms debounce if err != nil { fmt.Fprintf(os.Stderr, "Failed to create file watcher: %v\n", err) os.Exit(1) } defer watcher.Close() if err := watcher.DiscoverRequiredFiles(scriptPath); err != nil { fmt.Fprintf(os.Stderr, "Failed to watch files: %v\n", err) os.Exit(1) } restartCh := watcher.Start() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) fmt.Printf("Starting %s in watch mode...\n", scriptPath) var hadError bool firstRun := true for { // If we had an error on the last run and this isn't the first run, // wait for file changes before retrying if hadError && !firstRun { fmt.Println("Waiting for file changes before retrying...") select { case <-restartCh: fmt.Println("Files changed, retrying...") case <-sigChan: fmt.Println("\nExiting...") return } } firstRun = false hadError = false // Clear cache before each run state.ClearCache() // Create and run state luaState, err := state.NewFromScript(scriptPath) if err != nil { log.Printf("Error creating state: %v", err) hadError = true continue } if err := luaState.ExecuteFile(scriptPath); err != nil { log.Printf("Execution error: %v", err) luaState.Close() hadError = true continue } // If not a long-running process, wait for changes and restart if !http.HasActiveServers() { fmt.Println("Script completed. Waiting for changes...") luaState.Close() select { case <-restartCh: fmt.Println("Files changed, restarting...") continue case <-sigChan: fmt.Println("\nExiting...") return } } // Long-running process - wait for restart signal or exit signal fmt.Println("HTTP servers running. Watching for file changes...") select { case <-restartCh: fmt.Println("Files changed, restarting...") http.StopAllServers() luaState.Close() sql.CloseAllConnections() time.Sleep(100 * time.Millisecond) // Brief pause for cleanup continue case <-sigChan: fmt.Println("\nShutting down...") http.StopAllServers() luaState.Close() sql.CloseAllConnections() return } } }