Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions api/runstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package api

import (
"sync"
"time"

"github.com/iamangus/OpenDev/orchestrator"
)

// RunStatus represents the lifecycle state of an orchestration run.
type RunStatus string

const (
RunStatusPending RunStatus = "pending"
RunStatusRunning RunStatus = "running"
RunStatusDone RunStatus = "done"
RunStatusFailed RunStatus = "failed"
)

// Run holds the state of a single orchestration run.
type Run struct {
ID string `json:"id"`
Status RunStatus `json:"status"`
Request string `json:"request"`
Repo string `json:"repo"`
Spec *orchestrator.SystemSpec `json:"spec,omitempty"`
Features []orchestrator.Feature `json:"features,omitempty"`
Contracts []orchestrator.Contract `json:"contracts,omitempty"`
Jobs []featureLeadJobSummary `json:"jobs,omitempty"`
Error string `json:"error,omitempty"`
PRNumber int `json:"pr_number,omitempty"`
PRURL string `json:"pr_url,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

// RunStore is a thread-safe in-memory store for orchestration run state.
type RunStore struct {
mu sync.RWMutex
runs map[string]*Run
}

// NewRunStore creates an empty RunStore.
func NewRunStore() *RunStore {
return &RunStore{runs: make(map[string]*Run)}
}

// Create adds a new run in the pending state and returns it.
func (s *RunStore) Create(id, request, repo string) *Run {
now := time.Now()
r := &Run{
ID: id,
Status: RunStatusPending,
Request: request,
Repo: repo,
CreatedAt: now,
UpdatedAt: now,
}
s.mu.Lock()
s.runs[id] = r
s.mu.Unlock()
return r
}

// SetRunning marks the run as running.
func (s *RunStore) SetRunning(id string) {
s.mu.Lock()
defer s.mu.Unlock()
if r, ok := s.runs[id]; ok {
r.Status = RunStatusRunning
r.UpdatedAt = time.Now()
}
}

// SetDone marks the run as done and stores the orchestrator result.
func (s *RunStore) SetDone(id string, result *orchestrator.Result, jobs []featureLeadJobSummary) {
s.mu.Lock()
defer s.mu.Unlock()
if r, ok := s.runs[id]; ok {
r.Status = RunStatusDone
r.Spec = &result.Spec
r.Features = result.Features
r.Contracts = result.Contracts
r.Jobs = jobs
r.UpdatedAt = time.Now()
}
}

// SetFailed marks the run as failed with an error message.
func (s *RunStore) SetFailed(id string, err error) {
s.mu.Lock()
defer s.mu.Unlock()
if r, ok := s.runs[id]; ok {
r.Status = RunStatusFailed
r.Error = err.Error()
r.UpdatedAt = time.Now()
}
}

// SetPR stores the GitHub PR number and URL on the run.
func (s *RunStore) SetPR(id string, number int, url string) {
s.mu.Lock()
defer s.mu.Unlock()
if r, ok := s.runs[id]; ok {
r.PRNumber = number
r.PRURL = url
r.UpdatedAt = time.Now()
}
}

// Get returns the run for the given ID, or (nil, false) if not found.
func (s *RunStore) Get(id string) (*Run, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
r, ok := s.runs[id]
if !ok {
return nil, false
}
// Return a shallow copy to avoid races on the caller's side.
copy := *r
return &copy, true
}
Loading