github.com/golazy/golazy@v0.0.7-0.20221012133820-968fe65a0b65/lazydev/parent.go (about) 1 package lazydev 2 3 import ( 4 "log" 5 "net" 6 "net/http" 7 "os" 8 "os/exec" 9 "os/signal" 10 "path/filepath" 11 "strings" 12 "syscall" 13 "time" 14 15 "github.com/golazy/golazy/lazydev/filewatcher" 16 "github.com/golazy/golazy/lazydev/runner" 17 ) 18 19 type parent struct { 20 listenAddr string 21 } 22 23 var defaultListenAddr = ":3000" 24 25 func (s *parent) Serve(h http.Handler) error { 26 // listen Addr 27 addr := os.Getenv("PORT") 28 if addr == "" { 29 addr = defaultListenAddr 30 } else { 31 if !strings.Contains(addr, ":") { 32 addr = ":" + addr 33 } 34 } 35 36 tcpAddr, err := net.ResolveTCPAddr("tcp", addr) 37 if err != nil { 38 return err 39 } 40 l, err := net.ListenTCP("tcp", tcpAddr) 41 if err != nil { 42 return err 43 } 44 // Working directory 45 cmd, err := childCmd(l) 46 if err != nil { 47 return err 48 } 49 r := runner.New(cmd, nil) 50 51 running := false 52 err = r.Start() 53 if err != nil { 54 return err 55 } 56 defer func() { 57 if running { 58 r.Stop() 59 } 60 }() 61 62 fw, err := filewatcher.New("") 63 if err != nil { 64 return err 65 } 66 changeSet, err := fw.Watch() 67 if err != nil { 68 return err 69 } 70 defer func() { 71 fw.Close() 72 }() 73 74 intSignal := make(chan os.Signal, 1) 75 signal.Notify(intSignal, os.Interrupt) 76 77 for { 78 select { 79 case event := <-r.Events: 80 if _, ok := event.(runner.EventStarted); ok { 81 running = true 82 } 83 if _, ok := event.(runner.EventStopped); ok { 84 running = false 85 } 86 case cs := <-changeSet: 87 log.Printf("%T %+v", cs, cs) 88 err := r.Restart() 89 if err != nil { 90 log.Println("Error while restarting:", err) 91 } 92 93 case <-intSignal: 94 if running == false { 95 return nil 96 } 97 r.Stop() 98 wait := time.After(time.Second * 2) 99 intCount := 1 100 for wait != nil { 101 select { 102 case <-wait: 103 wait = nil 104 case event := <-r.Events: 105 log.Printf("%T %+v", event, event) 106 if _, ok := event.(runner.EventStopped); ok { 107 running = false 108 return nil 109 } 110 case <-intSignal: 111 if intCount == 1 { 112 log.Println("Killing the process") 113 intCount += 1 114 r.Signal(syscall.SIGKILL) 115 continue 116 } 117 return nil 118 } 119 } 120 return nil 121 } 122 } 123 } 124 125 func childCmd(l *net.TCPListener) (*exec.Cmd, error) { 126 wd, err := os.Getwd() 127 if err != nil { 128 wd = "." 129 } 130 131 p := "*.go" 132 if wd != "" { 133 p = filepath.Join(wd, p) 134 } 135 files, err := filepath.Glob(p) 136 if err != nil { 137 log.Fatal(err) 138 } 139 140 args := []string{"run"} 141 for _, file := range files { 142 if strings.HasSuffix(file, "_test.go") { 143 continue 144 } 145 args = append(args, file) 146 } 147 148 file, err := l.File() 149 if err != nil { 150 return nil, err 151 } 152 log.Println("Listener is", file) 153 cmd := exec.Command("go", args...) 154 cmd.ExtraFiles = []*os.File{file} 155 cmd.Env = os.Environ() 156 cmd.Env = append(cmd.Env, childEnvKey+"=true") 157 158 logger := log.New(os.Stdout, "child: ", 0) 159 logW := logWriter{logger} 160 cmd.Stdout = logW 161 cmd.Stderr = logW 162 163 return cmd, nil 164 } 165 166 type logWriter struct { 167 *log.Logger 168 } 169 170 func (l logWriter) Write(args []byte) (int, error) { 171 l.Println(string(args)) 172 return len(args), nil 173 }