From 945ca2e3dffe0cdcfc38b32e8b229a8ebec5d9be Mon Sep 17 00:00:00 2001 From: Ilya Groshev Date: Wed, 22 Apr 2026 21:47:51 +0300 Subject: [PATCH] Enhance dev runner with automatic service builds --- README.md | 5 ++++ server/Makefile | 23 ++++++++++++++- server/cmd/dev/main.go | 59 +++++++++++++++++++++++++++++++++++++-- server/cmd/wizard/main.go | 16 ++++++++++- 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b5a7592..dd5299e 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,8 @@ go run ./cmd/dev Each service's output is prefixed with a colored label (`[auth]`, `[cdn]`, `[grpc]`). Press Ctrl+C to shut everything down. +The dev runner automatically builds each service into `bin/` before launching. This means the binaries have stable file paths, so **Windows Firewall only prompts once** — subsequent runs reuse the same allowed executables. The wizard performs the same build step transparently. + Override defaults with namespaced flags: ```bash @@ -221,8 +223,11 @@ All targets run from the `server/` directory. | `make build` | Build the game server binary | | `make build-cdn` | Build the CDN binary | | `make build-auth` | Build the auth server binary | +| `make build-dev` | Build the dev runner binary to `bin/` | +| `make build-all` | Build all service binaries to `bin/` | | `make build-import` | Build the import-snapshot tool | | `make build-claim-account` | Build the claim-account tool | +| `make clean` | Remove the `bin/` directory | | `make dev` | Run all three services with one command | | `make migrate` | Run goose migrations on `db/game.db` | | `make import` | Import a snapshot (`SNAPSHOT=... UUID=...` required) | diff --git a/server/Makefile b/server/Makefile index 10b265c..503c34a 100644 --- a/server/Makefile +++ b/server/Makefile @@ -30,6 +30,27 @@ build-auth: build-claim-account: go build -o claim-account$(EXE) ./cmd/claim-account +build-dev: + go build -o bin/dev$(EXE) ./cmd/dev + +build-all: +ifeq ($(OS),Windows_NT) + if not exist bin mkdir bin +else + mkdir -p bin +endif + go build -o bin/dev$(EXE) ./cmd/dev + go build -o bin/auth-server$(EXE) ./cmd/auth-server + go build -o bin/octo-cdn$(EXE) ./cmd/octo-cdn + go build -o bin/lunar-tear$(EXE) ./cmd/lunar-tear + +clean: +ifeq ($(OS),Windows_NT) + if exist bin rmdir /s /q bin +else + rm -rf bin +endif + dev: go run ./cmd/dev $(ARGS) @@ -50,4 +71,4 @@ ifndef UUID endif go run ./cmd/import-snapshot --snapshot $(SNAPSHOT) --uuid $(UUID) -.PHONY: proto build build-cdn build-auth build-import build-claim-account dev migrate import +.PHONY: proto build build-cdn build-auth build-import build-claim-account build-dev build-all clean dev migrate import diff --git a/server/cmd/dev/main.go b/server/cmd/dev/main.go index 8168230..01781be 100644 --- a/server/cmd/dev/main.go +++ b/server/cmd/dev/main.go @@ -10,6 +10,8 @@ import ( "os" "os/exec" "os/signal" + "path/filepath" + "runtime" "sync" "syscall" ) @@ -28,6 +30,53 @@ type service struct { cmd *exec.Cmd } +func binExt() string { + if runtime.GOOS == "windows" { + return ".exe" + } + return "" +} + +func buildAll() { + if err := os.MkdirAll("bin", 0755); err != nil { + log.Fatalf("create bin/: %v", err) + } + + type target struct { + name string + pkg string + } + targets := []target{ + {"auth-server", "./cmd/auth-server"}, + {"octo-cdn", "./cmd/octo-cdn"}, + {"lunar-tear", "./cmd/lunar-tear"}, + } + + ext := binExt() + var wg sync.WaitGroup + errs := make(chan error, len(targets)) + + for _, t := range targets { + wg.Add(1) + go func(t target) { + defer wg.Done() + out := filepath.Join("bin", t.name+ext) + cmd := exec.Command("go", "build", "-o", out, t.pkg) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + errs <- fmt.Errorf("build %s: %w", t.name, err) + } + }(t) + } + wg.Wait() + close(errs) + + for err := range errs { + log.Fatal(err) + } +} + func main() { // auth-server flags authListen := flag.String("auth.listen", "0.0.0.0:3000", "auth-server listen address (host:port)") @@ -62,6 +111,10 @@ func main() { colorCyan = "" } + log.Println("building services...") + buildAll() + + ext := binExt() ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() @@ -69,7 +122,7 @@ func main() { { label: "auth", color: colorGreen, - cmd: exec.CommandContext(ctx, "go", "run", "./cmd/auth-server", + cmd: exec.CommandContext(ctx, filepath.Join("bin", "auth-server"+ext), "--listen", *authListen, "--db", *authDB, ), @@ -77,7 +130,7 @@ func main() { { label: "cdn", color: colorCyan, - cmd: exec.CommandContext(ctx, "go", "run", "./cmd/octo-cdn", + cmd: exec.CommandContext(ctx, filepath.Join("bin", "octo-cdn"+ext), "--listen", *cdnListen, "--public-addr", *cdnPublicAddr, ), @@ -85,7 +138,7 @@ func main() { { label: "grpc", color: colorYellow, - cmd: exec.CommandContext(ctx, "go", "run", "./cmd/lunar-tear", + cmd: exec.CommandContext(ctx, filepath.Join("bin", "lunar-tear"+ext), "--listen", *grpcListen, "--public-addr", *grpcPublicAddr, "--db", *grpcDB, diff --git a/server/cmd/wizard/main.go b/server/cmd/wizard/main.go index c838d78..fc9d947 100644 --- a/server/cmd/wizard/main.go +++ b/server/cmd/wizard/main.go @@ -795,7 +795,21 @@ func saveConfig(cfg config) { } func launchDev(ip string, p ports) { - cmd := exec.Command("go", "run", "./cmd/dev", + ext := "" + if runtime.GOOS == "windows" { + ext = ".exe" + } + devBin := filepath.Join("bin", "dev"+ext) + + _ = spinner.New().Title(" Building services...").Action(func() { + if err := os.MkdirAll("bin", 0755); err != nil { + fmt.Fprintf(os.Stderr, " Failed to create bin/: %v\n", err) + os.Exit(1) + } + runQuiet(exec.Command("go", "build", "-o", devBin, "./cmd/dev"), "build dev") + }).Run() + + cmd := exec.Command(devBin, "--grpc.listen", fmt.Sprintf("0.0.0.0:%d", p.GRPC), "--grpc.public-addr", fmt.Sprintf("%s:%d", ip, p.GRPC), "--cdn.listen", fmt.Sprintf("0.0.0.0:%d", p.CDN),