Files
ForgeBucket/cmd/forgebucket/main.go
T
2026-05-12 22:34:26 +02:00

149 lines
3.8 KiB
Go

package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gorilla/sessions"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
"github.com/forgeo/forgebucket/internal/api"
"github.com/forgeo/forgebucket/internal/config"
"github.com/forgeo/forgebucket/internal/db"
"github.com/forgeo/forgebucket/internal/domain/ci"
gitdomain "github.com/forgeo/forgebucket/internal/domain/git"
"github.com/forgeo/forgebucket/internal/domain/gitops"
"github.com/forgeo/forgebucket/internal/domain/oci"
"github.com/forgeo/forgebucket/internal/domain/scanning"
"github.com/forgeo/forgebucket/internal/domain/vulnscan"
"github.com/forgeo/forgebucket/internal/domain/sbom"
"github.com/forgeo/forgebucket/internal/domain/signing"
"github.com/forgeo/forgebucket/internal/events"
"github.com/forgeo/forgebucket/internal/observability"
"github.com/forgeo/forgebucket/internal/models/migrations"
"github.com/forgeo/forgebucket/web"
)
func main() {
_ = godotenv.Load()
cfg, err := config.Load()
if err != nil {
log.Fatalf("config: %v", err)
}
engine, err := db.Open(cfg.DatabaseURL, cfg.Debug)
if err != nil {
log.Fatalf("database: %v", err)
}
defer engine.Close()
if err := migrations.Run(engine); err != nil {
log.Fatalf("migrations: %v", err)
}
gitdomain.SetRepoRoot(cfg.RepoRoot)
if err := os.MkdirAll(cfg.ArtifactRoot, 0755); err != nil {
log.Fatalf("artifact root: %v", err)
}
ociRegistry, err := oci.New(cfg.OCIRoot)
if err != nil {
log.Fatalf("oci: %v", err)
}
log.Printf("oci: registry initialised at %s", cfg.OCIRoot)
bus, err := events.New(cfg.NATSUrl)
if err != nil {
log.Fatalf("events: %v", err)
}
defer bus.Close()
store := sessions.NewCookieStore([]byte(cfg.SessionSecret))
store.Options = &sessions.Options{
Path: "/",
MaxAge: 86400 * 7,
HttpOnly: true,
Secure: !cfg.Debug,
SameSite: http.SameSiteLaxMode,
}
// Start CI orchestrator and runner manager in background goroutines.
ciCtx, ciCancel := context.WithCancel(context.Background())
defer ciCancel()
orchestrator := ci.NewOrchestrator(engine, bus)
go orchestrator.Start(ciCtx)
runnerMgr := ci.NewRunnerManager(engine, bus, cfg, 4)
go runnerMgr.Start(ciCtx)
gitopsCtrl := gitops.NewController(engine, bus, cfg)
go gitopsCtrl.Start(ciCtx)
sbomGen := sbom.NewGenerator(engine, bus)
go sbomGen.Start(ciCtx)
go observability.StartNATSWatcher(ciCtx, bus)
// Initialise artifact signing key store.
var keyStore *signing.KeyStore
if cfg.ArtifactSigningKey != "" {
keyStore, err = signing.New(cfg.ArtifactSigningKey)
if err != nil {
log.Fatalf("signing: %v", err)
}
} else {
keyStore, err = signing.Generate()
if err != nil {
log.Fatalf("signing: %v", err)
}
}
log.Printf("signing: key store initialised (keyId=%s)", keyStore.KeyID())
secretScanner, err := scanning.New(engine, bus)
if err != nil {
log.Fatalf("secret scanner: %v", err)
}
go secretScanner.Start(ciCtx)
vulnScanner := vulnscan.NewScanner(engine, bus)
go vulnScanner.Start(ciCtx)
handler := api.New(cfg, engine, store, bus, cfg.ArtifactRoot, web.FS(), *keyStore, sbomGen, ociRegistry, secretScanner, vulnScanner)
srv := &http.Server{
Addr: fmt.Sprintf(":%s", cfg.Port),
Handler: handler,
ReadTimeout: 15 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 120 * time.Second,
}
go func() {
log.Printf("ForgeBucket listening on http://localhost:%s", cfg.Port)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("server: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("shutdown: %v", err)
}
log.Println("ForgeBucket stopped")
}