Files
byre/cmd/byre/serve.go
T
2026-10-18 23:07:11 -07:00

95 lines
2.1 KiB
Go

package main
import (
"context"
"errors"
"flag"
"log/slog"
"net"
"net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"g.fc.run/ai/byre/internal/server"
"g.fc.run/ai/byre/internal/store"
)
const defaultListen = "127.0.0.1:8270"
func serveCmd(args []string) {
configDir, err := os.UserConfigDir()
if err != nil {
fatal("user config dir", "err", err)
}
flags := flag.NewFlagSet("serve", flag.ExitOnError)
dataDir := flags.String("data", filepath.Join(configDir, "byre"), "directory holding byre's database, worktrees, leads and blobs")
listenAddr := flags.String("listen", defaultListen, "address to listen on")
ports := flags.Int("ports", store.DefaultPortFirst, "first port of the tasks' port blocks")
flags.Parse(args)
srv, err := server.New(*dataDir, *ports)
if err != nil {
fatal("start server", "err", err)
}
defer srv.Close()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
ctx, replaced := context.WithCancel(ctx)
defer replaced()
go watchExecutable(ctx, replaced)
hs := &http.Server{
Addr: *listenAddr,
Handler: srv.Handler(),
BaseContext: func(net.Listener) context.Context { return ctx },
}
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
hs.Shutdown(shutdownCtx)
}()
slog.Info("listening", "addr", *listenAddr, "data", *dataDir, "ports", *ports)
err = hs.ListenAndServe()
if !errors.Is(err, http.ErrServerClosed) {
fatal("serve", "err", err)
}
}
func watchExecutable(ctx context.Context, replaced context.CancelFunc) {
exe, err := os.Executable()
if err != nil {
fatal("executable path", "err", err)
}
started, err := os.Stat(exe)
if err != nil {
fatal("stat executable", "path", exe, "err", err)
}
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
}
now, err := os.Stat(exe)
if err != nil {
continue
}
if os.SameFile(started, now) {
continue
}
slog.Info("executable replaced, exiting", "path", exe)
replaced()
return
}
}