Skip to content

Instantly share code, notes, and snippets.

@torniker
Last active May 17, 2025 16:24
Show Gist options
  • Save torniker/c2dfa3e4aa3fc3331e8679437e4ee7a1 to your computer and use it in GitHub Desktop.
Save torniker/c2dfa3e4aa3fc3331e8679437e4ee7a1 to your computer and use it in GitHub Desktop.
func Start(name string, handler http.Handler, port string) {
if port == "" {
panic("port not provided")
}
srv := http.Server{
Addr: ":" + port,
Handler: handler,
}
log.Printf("Listening on :%s", port)
go func() {
start(srv.ListenAndServe, name)
log.Println("Stopped serving new connections.")
}()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 30*time.Second)
defer shutdownRelease()
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Fatalf("HTTP shutdown error: %v", err)
}
log.Println("Graceful shutdown complete.")
}
// Start web server
func start(start Starter, name string) {
var err error
basePath, err = os.Getwd()
if err != nil {
if err := start(); err != nil {
log.Printf("shutting down, error: %s\n", err)
}
return
}
binPath = fmt.Sprintf("%s/bin/%s", basePath, name)
binFilename = filepath.Base(binPath)
mainGoPath = fmt.Sprintf("%s/cmd/%s", basePath, binFilename)
if os.Getenv("ENV") == "local" {
autorestart.WatchFilename = binPath
restart := autorestart.GetNotifier()
go func() {
<-restart
fmt.Println("\033[36mI will restart shortly\033[00m")
spin.Stop()
}()
if fileExists(autorestart.WatchFilename) {
autorestart.StartWatcher()
go watch()
} else {
fmt.Println("watcher did not start")
}
}
if err := start(); err != nil {
log.Printf("shutting down the server, error: %s\n", err)
}
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func rebuild() {
cmd := exec.Command("go", "build", "-o", binPath, mainGoPath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
// Watch file changes and restart server
func watch() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write {
if filepath.Ext(event.Name) == ".go" {
spin.Color("blue")
spin.Suffix = "\033[33m Building... \033[00m"
spin.Start()
rebuild()
}
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Fatal(err)
}
}
}()
err = filepath.Walk(basePath,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && filepath.Ext(info.Name()) == ".go" {
err = watcher.Add(filepath.Dir(path))
if err != nil {
log.Fatal(err)
}
}
return nil
},
)
if err != nil {
log.Fatal(err)
}
<-done
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment