Implements horizontally-scalable worker pool architecture: - claudebox-sidecar: HTTP server for Claude Code, git, and SDLC ops - rdev-worker: standalone worker binary polling rdev-api for tasks - HTTP client adapter for sidecar communication - HPA with custom Prometheus metrics for autoscaling - ServiceMonitor for metrics scraping Code review fixes applied: - URL-encode query parameters in GitStatus (Critical #1) - Remove unused shellQuote function (Critical #2) - Use stdlib strings.Split/TrimSpace (Critical #3) - Add version injection via ldflags (Warning #4) - Add debug logging for swallowed git/sdlc errors (Warning #5, #6) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
112 lines
2.8 KiB
Go
112 lines
2.8 KiB
Go
// Package main provides the claudebox-sidecar HTTP server.
|
|
// This sidecar runs alongside Claude Code in worker pods, exposing HTTP endpoints
|
|
// for execute, git, and SDLC operations - replacing kubectl exec calls.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/orchard9/rdev/internal/claudebox"
|
|
"github.com/orchard9/rdev/internal/envutil"
|
|
"github.com/orchard9/rdev/internal/logging"
|
|
)
|
|
|
|
func main() {
|
|
// Configure logging
|
|
logLevel := logging.LevelInfo
|
|
if envutil.GetEnvBool("DEBUG", false) {
|
|
logLevel = logging.LevelDebug
|
|
}
|
|
log := logging.New(logging.Config{
|
|
Level: logLevel,
|
|
Format: logging.FormatJSON,
|
|
})
|
|
|
|
// Configuration from environment
|
|
port := envutil.GetEnv("PORT", "8080")
|
|
workDir := envutil.GetEnv("WORKSPACE_DIR", "/workspace")
|
|
giteaToken := os.Getenv("GITEA_TOKEN") // Required for git push auth
|
|
gitUser := envutil.GetEnv("GIT_USER", "rdev-worker")
|
|
gitEmail := envutil.GetEnv("GIT_EMAIL", "worker@threesix.ai")
|
|
|
|
// Create server components
|
|
executor := claudebox.NewExecutor(workDir)
|
|
gitOps := claudebox.NewGitOperations(claudebox.GitOperationsConfig{
|
|
WorkDir: workDir,
|
|
GiteaToken: giteaToken,
|
|
GitUser: gitUser,
|
|
GitEmail: gitEmail,
|
|
Logger: log.Slog(),
|
|
})
|
|
sdlcRunner := claudebox.NewSDLCRunner(claudebox.SDLCRunnerConfig{
|
|
WorkDir: workDir,
|
|
Logger: log.Slog(),
|
|
})
|
|
|
|
// Create the server
|
|
server := claudebox.NewServer(claudebox.ServerConfig{
|
|
Executor: executor,
|
|
GitOps: gitOps,
|
|
SDLCRunner: sdlcRunner,
|
|
Logger: log.Slog(),
|
|
})
|
|
|
|
// Create router
|
|
r := chi.NewRouter()
|
|
r.Use(middleware.RequestID)
|
|
r.Use(middleware.RealIP)
|
|
r.Use(logging.Middleware(logging.MiddlewareConfig{
|
|
Logger: log,
|
|
}))
|
|
r.Use(middleware.Recoverer)
|
|
r.Use(middleware.Timeout(10 * time.Minute))
|
|
|
|
// Mount server routes
|
|
server.Mount(r)
|
|
|
|
// Create HTTP server
|
|
addr := fmt.Sprintf(":%s", port)
|
|
httpServer := &http.Server{
|
|
Addr: addr,
|
|
Handler: r,
|
|
ReadTimeout: 30 * time.Second,
|
|
WriteTimeout: 15 * time.Minute, // Long timeout for streaming responses
|
|
IdleTimeout: 60 * time.Second,
|
|
}
|
|
|
|
// Start server in goroutine
|
|
go func() {
|
|
log.Info("starting claudebox-sidecar", "addr", addr, "workDir", workDir)
|
|
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Error("server error", logging.FieldError, err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
// Wait for shutdown signal
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
log.Info("shutting down server")
|
|
|
|
// Graceful shutdown with timeout
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
if err := httpServer.Shutdown(ctx); err != nil {
|
|
log.Error("server shutdown error", logging.FieldError, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
log.Info("server stopped")
|
|
}
|