forked from adamm/autogits
215 lines
4.8 KiB
Go
215 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"src.opensuse.org/autogits/common"
|
|
)
|
|
|
|
var RepoStatus []*common.BuildResult = []*common.BuildResult{}
|
|
var RepoStatusLock *sync.RWMutex = &sync.RWMutex{}
|
|
|
|
var redisClient *redis.Client
|
|
|
|
func RedisConnect(RedisUrl string) {
|
|
opts, err := redis.ParseURL(RedisUrl)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
redisClient = redis.NewClient(opts)
|
|
}
|
|
|
|
func UpdateResults(r *common.BuildResult) {
|
|
RepoStatusLock.Lock()
|
|
defer RepoStatusLock.Unlock()
|
|
|
|
key := "result." + r.Project + "/" + r.Repository + "/" + r.Arch
|
|
common.LogDebug(" + Updating", key)
|
|
data, err := redisClient.HGetAll(context.Background(), key).Result()
|
|
if err != nil {
|
|
common.LogError("Failed fetching build results for", key, err)
|
|
}
|
|
common.LogDebug(" + Update size", len(data))
|
|
|
|
reset_time := time.Date(1000, 1, 1, 1, 1, 1, 1, time.Local)
|
|
for _, pkg := range r.Status {
|
|
pkg.LastUpdate = reset_time
|
|
}
|
|
r.LastUpdate = time.Now()
|
|
for pkg, result := range data {
|
|
if strings.HasPrefix(result, "scheduled") {
|
|
// TODO: lookup where's building
|
|
result = "building"
|
|
}
|
|
|
|
var idx int
|
|
var found bool
|
|
var code string
|
|
var details string
|
|
|
|
if pos := strings.IndexByte(result, ':'); pos > -1 && pos < len(result) {
|
|
code = result[0:pos]
|
|
details = result[pos+1:]
|
|
} else {
|
|
code = result
|
|
details = ""
|
|
}
|
|
|
|
if idx, found = slices.BinarySearchFunc(r.Status, &common.PackageBuildStatus{Package: pkg}, common.PackageBuildStatusComp); found {
|
|
res := r.Status[idx]
|
|
res.LastUpdate = r.LastUpdate
|
|
res.Code = code
|
|
res.Details = details
|
|
} else {
|
|
r.Status = slices.Insert(r.Status, idx, &common.PackageBuildStatus{
|
|
Package: pkg,
|
|
Code: code,
|
|
Details: details,
|
|
LastUpdate: r.LastUpdate,
|
|
})
|
|
}
|
|
}
|
|
for idx := 0; idx < len(r.Status); {
|
|
if r.Status[idx].LastUpdate == reset_time {
|
|
r.Status = slices.Delete(r.Status, idx, idx+1)
|
|
} else {
|
|
idx++
|
|
}
|
|
}
|
|
}
|
|
|
|
func FindProjectResults(project string) []*common.BuildResult {
|
|
RepoStatusLock.RLock()
|
|
defer RepoStatusLock.RUnlock()
|
|
|
|
ret := make([]*common.BuildResult, 0, 8)
|
|
idx, _ := slices.BinarySearchFunc(RepoStatus, &common.BuildResult{Project: project}, common.BuildResultComp)
|
|
for idx < len(RepoStatus) && RepoStatus[idx].Project == project {
|
|
ret = append(ret, RepoStatus[idx])
|
|
idx++
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func FindRepoResults(project, repo string) []*common.BuildResult {
|
|
RepoStatusLock.RLock()
|
|
defer RepoStatusLock.RUnlock()
|
|
|
|
ret := make([]*common.BuildResult, 0, 8)
|
|
idx, _ := slices.BinarySearchFunc(RepoStatus, &common.BuildResult{Project: project, Repository: repo}, common.BuildResultComp)
|
|
for idx < len(RepoStatus) && RepoStatus[idx].Project == project && RepoStatus[idx].Repository == repo {
|
|
ret = append(ret, RepoStatus[idx])
|
|
idx++
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func FindAndUpdateProjectResults(project string) []*common.BuildResult {
|
|
res := FindProjectResults(project)
|
|
wg := &sync.WaitGroup{}
|
|
now := time.Now()
|
|
for _, r := range res {
|
|
if now.Sub(r.LastUpdate).Abs() < time.Second*10 {
|
|
// 1 update per 10 second for now
|
|
continue
|
|
}
|
|
wg.Add(1)
|
|
go func() {
|
|
UpdateResults(r)
|
|
wg.Done()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
return res
|
|
}
|
|
|
|
func FindAndUpdateRepoResults(project, repo string) []*common.BuildResult {
|
|
res := FindRepoResults(project, repo)
|
|
wg := &sync.WaitGroup{}
|
|
now := time.Now()
|
|
for _, r := range res {
|
|
if now.Sub(r.LastUpdate).Abs() < time.Second*10 {
|
|
// 1 update per 10 second for now
|
|
continue
|
|
}
|
|
wg.Add(1)
|
|
go func() {
|
|
UpdateResults(r)
|
|
wg.Done()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
return res
|
|
}
|
|
|
|
func RescanRepositories() error {
|
|
ctx := context.Background()
|
|
var cursor uint64
|
|
var err error
|
|
|
|
common.LogDebug("** starting rescanning ...")
|
|
RepoStatusLock.Lock()
|
|
for _, repo := range RepoStatus {
|
|
repo.Dirty = false
|
|
}
|
|
RepoStatusLock.Unlock()
|
|
var count int
|
|
|
|
for {
|
|
var data []string
|
|
data, cursor, err = redisClient.ScanType(ctx, cursor, "", 1000, "hash").Result()
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
RepoStatusLock.Lock()
|
|
for _, repo := range data {
|
|
r := strings.Split(repo, "/")
|
|
if len(r) != 3 || len(r[0]) < 8 || r[0][0:7] != "result." {
|
|
continue
|
|
}
|
|
d := &common.BuildResult{
|
|
Project: r[0][7:],
|
|
Repository: r[1],
|
|
Arch: r[2],
|
|
}
|
|
if pos, found := slices.BinarySearchFunc(RepoStatus, d, common.BuildResultComp); found {
|
|
RepoStatus[pos].Dirty = true
|
|
} else {
|
|
d.Dirty = true
|
|
RepoStatus = slices.Insert(RepoStatus, pos, d)
|
|
count++
|
|
}
|
|
}
|
|
RepoStatusLock.Unlock()
|
|
|
|
if cursor == 0 {
|
|
break
|
|
}
|
|
}
|
|
common.LogDebug(" added a total", count, "repos")
|
|
count = 0
|
|
|
|
RepoStatusLock.Lock()
|
|
for i := 0; i < len(RepoStatus); {
|
|
if !RepoStatus[i].Dirty {
|
|
RepoStatus = slices.Delete(RepoStatus, i, i+1)
|
|
count++
|
|
} else {
|
|
i++
|
|
}
|
|
}
|
|
RepoStatusLock.Unlock()
|
|
common.LogDebug(" removed", count, "repos")
|
|
common.LogDebug(" total repos:", len(RepoStatus))
|
|
|
|
return nil
|
|
}
|