Add errored builds api. fix package parsing of source version numbers

This commit is contained in:
ferreo 2024-07-30 15:41:35 +01:00
parent eaae782180
commit 6c31af0166
9 changed files with 175 additions and 25 deletions

View File

@ -3,6 +3,7 @@ package buildqueue
import ( import (
"brunel/config" "brunel/config"
"brunel/domain" "brunel/domain"
"brunel/helpers"
"brunel/packages" "brunel/packages"
"context" "context"
"fmt" "fmt"
@ -30,7 +31,6 @@ func StartPackageQueueWorker(ctx context.Context) {
packs.ForEach(func(k string, v domain.SourcePackage) bool { packs.ForEach(func(k string, v domain.SourcePackage) bool {
needsBuild := false needsBuild := false
buildVersion := "" buildVersion := ""
buildAttempt := 0
v.Packages.ForEach(func(k string, v domain.PackageInfo) bool { v.Packages.ForEach(func(k string, v domain.PackageInfo) bool {
if v.Status == domain.Current { if v.Status == domain.Current {
return true return true
@ -39,11 +39,6 @@ func StartPackageQueueWorker(ctx context.Context) {
if vs == "" { if vs == "" {
vs = v.Version vs = v.Version
} }
if v.LastBuildStatus == domain.Error {
if v.BuildAttempts > 0 {
buildAttempt = v.BuildAttempts
}
}
if v.Status == domain.Missing || v.Status == domain.Stale { if v.Status == domain.Missing || v.Status == domain.Stale {
needsBuild = true needsBuild = true
} }
@ -66,11 +61,17 @@ func StartPackageQueueWorker(ctx context.Context) {
return true return true
}) })
if needsBuild { if needsBuild {
if buildAttempt > 1 { state, err := helpers.DBInst.GetBuildState(v.Name)
if err != nil {
state = domain.BuildState{
BuildNumber: 0,
}
}
if state.BuildNumber > 1 {
return true return true
} }
typ := domain.BuildTypeLTO typ := domain.BuildTypeLTO
if buildAttempt == 1 { if state.BuildNumber == 1 {
typ = domain.BuildTypeNormal typ = domain.BuildTypeNormal
} }
buildItem := domain.BuildQueueItem{ buildItem := domain.BuildQueueItem{
@ -79,10 +80,10 @@ func StartPackageQueueWorker(ctx context.Context) {
Type: typ, Type: typ,
Patch: false, Patch: false,
Rebuild: false, Rebuild: false,
BuildNumber: buildAttempt, BuildNumber: state.BuildNumber,
BuildVersion: buildVersion, BuildVersion: buildVersion,
} }
err := Add(buildItem) err = Add(buildItem)
if err != nil { if err != nil {
slog.Info("unable to add package to queue: " + err.Error()) slog.Info("unable to add package to queue: " + err.Error())
} }
@ -113,10 +114,13 @@ func processQueueAndStatus(ctx context.Context) {
slog.Error("unable to check if build is complete: " + err.Error()) slog.Error("unable to check if build is complete: " + err.Error())
} }
if complete { if complete {
if err != nil { if err != nil {
updatePackageStatus(&item, domain.Error, domain.Error) updateBuildState(item, domain.Error)
updatePackageStatus(&item, domain.Error)
} else { } else {
updatePackageStatus(&item, domain.Current, domain.Built) updateBuildState(item, domain.Built)
updatePackageStatus(&item, domain.Current)
} }
packages.UpdateSourcePackage(item.Source) packages.UpdateSourcePackage(item.Source)
itemsToRemove = append(itemsToRemove, k) itemsToRemove = append(itemsToRemove, k)
@ -136,19 +140,16 @@ func processQueueAndStatus(ctx context.Context) {
} }
} }
time.Sleep(1500 * time.Millisecond) time.Sleep(10 * time.Second)
} }
} }
} }
func updatePackageStatus(item *domain.BuildQueueItem, status domain.PackageStatus, buildStatus domain.PackageStatus) { func updatePackageStatus(item *domain.BuildQueueItem, status domain.PackageStatus) {
item.Source.Packages.ForEach(func(k string, v domain.PackageInfo) bool { item.Source.Packages.ForEach(func(k string, v domain.PackageInfo) bool {
v.Status = status v.Status = status
v.BuildAttempts++
v.LastBuildStatus = domain.PackageStatus(buildStatus)
if status == domain.Current { if status == domain.Current {
v.Version = item.BuildVersion v.Version = item.BuildVersion
v.BuildAttempts = 0
v.NewVersion = "" v.NewVersion = ""
} }
item.Source.Packages.Set(k, v) item.Source.Packages.Set(k, v)
@ -156,6 +157,22 @@ func updatePackageStatus(item *domain.BuildQueueItem, status domain.PackageStatu
}) })
} }
func updateBuildState(item domain.BuildQueueItem, buildStatus domain.PackageStatus) {
state, err := helpers.DBInst.GetBuildState(item.Source.Name)
if err != nil {
state = domain.BuildState{
Name: item.Source.Name,
Status: string(domain.Queued),
BuildVersion: item.BuildVersion,
BuildNumber: 0,
}
}
state.Status = string(buildStatus)
state.BuildVersion = item.BuildVersion
state.BuildNumber++
helpers.DBInst.UpdateBuildState(state)
}
func StartQueueAndStatusWorker(ctx context.Context) { func StartQueueAndStatusWorker(ctx context.Context) {
go processQueueAndStatus(ctx) go processQueueAndStatus(ctx)
} }

View File

@ -32,6 +32,7 @@ func New() (*gorm.DB, error) {
panic("failed to connect database") panic("failed to connect database")
} }
db.AutoMigrate(&domain.BuildState{})
db.AutoMigrate(&domain.User{}) db.AutoMigrate(&domain.User{})
db.AutoMigrate(&domain.Session{}) db.AutoMigrate(&domain.Session{})
db.AutoMigrate(&domain.PackageInfo{}) db.AutoMigrate(&domain.PackageInfo{})

View File

@ -138,6 +138,23 @@ func (r *Repository) UpdateLastUpdateTime(time time.Time) error {
return tx.Error return tx.Error
} }
func (r *Repository) UpdateBuildState(state domain.BuildState) error {
tx := r.db.Save(&state)
return tx.Error
}
func (r *Repository) GetBuildState(name string) (domain.BuildState, error) {
var state domain.BuildState
tx := r.db.Where("name = ?", name).First(&state)
return state, tx.Error
}
func (r *Repository) GetFailedBuilds() ([]domain.BuildState, error) {
var states []domain.BuildState
tx := r.db.Where("status = ? AND build_number > 1", string(domain.Error)).Find(&states)
return states, tx.Error
}
func sourcePackageToDto(pkg domain.SourcePackage) domain.SourcePackageDTO { func sourcePackageToDto(pkg domain.SourcePackage) domain.SourcePackageDTO {
dto := domain.SourcePackageDTO{ dto := domain.SourcePackageDTO{
Name: pkg.Name, Name: pkg.Name,

View File

@ -33,3 +33,10 @@ type BuildQueueCount struct {
Queued int `json:"queued"` Queued int `json:"queued"`
Building int `json:"building"` Building int `json:"building"`
} }
type BuildState struct {
Name string `gorm:"primarykey"`
Status string
BuildVersion string
BuildNumber int
}

2
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/samber/slog-fiber v1.16.0 github.com/samber/slog-fiber v1.16.0
github.com/ulikunitz/xz v0.5.12 github.com/ulikunitz/xz v0.5.12
golang.org/x/crypto v0.21.0 golang.org/x/crypto v0.21.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.22.0 golang.org/x/net v0.22.0
gorm.io/driver/sqlite v1.5.6 gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.11 gorm.io/gorm v1.25.11
@ -50,7 +51,6 @@ require (
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/mod v0.12.0 // indirect golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.18.0 // indirect golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect

29
handlers/build/errored.go Normal file
View File

@ -0,0 +1,29 @@
package handlers_build
import (
"brunel/domain"
"brunel/helpers"
"github.com/gofiber/fiber/v2"
"golang.org/x/exp/slog"
)
type ErroredResponse struct {
Total int `json:"total"`
Packages []domain.BuildState `json:"packages"`
}
func Errored(c *fiber.Ctx) error {
states, err := helpers.DBInst.GetFailedBuilds()
if err != nil {
slog.Error(err.Error())
return c.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
}
response := ErroredResponse{
Total: len(states),
Packages: states,
}
return c.Status(fiber.StatusOK).JSON(response)
}

View File

@ -0,0 +1,65 @@
package handlers_build
import (
"brunel/buildqueue"
"brunel/domain"
"brunel/packages"
"github.com/gofiber/fiber/v2"
)
func TriggerBuild(c *fiber.Ctx) error {
var req struct {
PackageName string `json:"packageName"`
Version string `json:"version"`
BuildType string `json:"buildType"`
Rebuild bool `json:"rebuild"`
}
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request body",
})
}
if req.PackageName == "" || req.Version == "" || req.BuildType == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Missing required fields",
})
}
packs := packages.GetPackages()
pack, ok := packs.Get(req.PackageName)
if !ok {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Package not found",
})
}
buildItem := domain.BuildQueueItem{
Source: pack,
BuildVersion: req.Version,
Type: domain.BuildType(req.BuildType),
Rebuild: req.Rebuild,
Status: domain.Queued,
Patch: false,
BuildNumber: 0,
}
err := buildqueue.Add(buildItem)
if err != nil {
if err.Error() == "package already in queue" {
return c.Status(fiber.StatusConflict).JSON(fiber.Map{
"error": "Package already in queue",
})
}
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Failed to add build to queue",
})
}
return c.Status(fiber.StatusAccepted).JSON(fiber.Map{
"message": "Build job added to queue",
"package": req.PackageName,
})
}

View File

@ -409,7 +409,25 @@ func fetchPackageFile(pkg config.PackageFile, selectedRepo string) (*haxmap.Map[
continue continue
} }
ver, err := version.Parse(stanza["Version"]) sourceSplit := strings.Split(stanza["Source"], " ")
source := sourceSplit[0]
if source == "" {
source = name
}
// Extract version from Source if available
sourceVersion := ""
if len(sourceSplit) > 1 {
sourceVersion = strings.Trim(sourceSplit[1], "()")
}
// Use sourceVersion if available, otherwise use stanza["Version"]
versionStr := stanza["Version"]
if sourceVersion != "" {
versionStr = sourceVersion
}
ver, err := version.Parse(versionStr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -423,12 +441,6 @@ func fetchPackageFile(pkg config.PackageFile, selectedRepo string) (*haxmap.Map[
} }
} }
sourceSplit := strings.Split(stanza["Source"], " ")
source := sourceSplit[0]
if source == "" {
source = name
}
packages.Set(name, domain.PackageInfo{ packages.Set(name, domain.PackageInfo{
PackageName: name, PackageName: name,
Version: ver.String(), Version: ver.String(),

View File

@ -93,8 +93,10 @@ func runServer(ctx context.Context) error {
server.Get("/api/counts", handlers_packages.Counts) server.Get("/api/counts", handlers_packages.Counts)
server.Get("/api/packages", handlers_packages.Packages) server.Get("/api/packages", handlers_packages.Packages)
server.Get("/api/queue", handlers_build.Queue) server.Get("/api/queue", handlers_build.Queue)
server.Get("/api/errored", handlers_build.Errored)
server.Post("/api/login", handlers_auth.Login) server.Post("/api/login", handlers_auth.Login)
adminRoutes.Post("/triggerBuild", handlers_build.TriggerBuild)
adminRoutes.Post("/register", handlers_auth.Register) adminRoutes.Post("/register", handlers_auth.Register)
adminRoutes.Post("/updatePassword", handlers_auth.UpdatePassword) adminRoutes.Post("/updatePassword", handlers_auth.UpdatePassword)