// Package cli implements custard's subcommands: the long-running forge server // (serve) and the local client verbs (check/preview/promote/release). package cli import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" "custard/internal/config" "custard/internal/server" ) // Serve runs the forge HTTP server until interrupted, then drains in-flight // requests. This is what the droplet runs (via systemd). func Serve(args []string) { cfg := config.Load(args) srv, err := server.New(cfg) if err != nil { log.Fatalf("init: %v", err) } httpSrv := &http.Server{ Addr: cfg.ListenAddr, Handler: srv.Handler(), ReadHeaderTimeout: 10 * time.Second, } go func() { log.Printf("custard listening on %s (repos: %s)", cfg.ListenAddr, cfg.ReposPath) if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatalf("server error: %v", err) } }() stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) <-stop ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := httpSrv.Shutdown(ctx); err != nil { log.Printf("shutdown error: %v", err) } log.Println("custard stopped") }