Composable monorepo CI fixes: - Add empty go.sum.tmpl files for pkg, service, worker, and cli components - Fix Dockerfile.tmpl glob patterns (COPY go.work.sum* is invalid in Kaniko) - Add deps step to CI that runs go work sync and go mod tidy before builds - Fix scalar-go dependency version (v0.1.2 doesn't exist, use v0.13.0) Health endpoint improvements: - Add registry health check (zot OCI /v2/ endpoint) - Add health metrics for CI, registry, and Git - Add /health/ci endpoint for Woodpecker health Visual verification scaffolding: - Add Playwright pod and scripts ConfigMap - Add vision.md and implementation breakdown plan Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
215 lines
5.8 KiB
Go
215 lines
5.8 KiB
Go
package domain
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// OperationType represents the type of operation.
|
|
type OperationType string
|
|
|
|
const (
|
|
OperationTypeProjectCreate OperationType = "project.create"
|
|
OperationTypeComponentAdd OperationType = "component.add"
|
|
OperationTypeBuild OperationType = "build"
|
|
OperationTypeCIBuild OperationType = "ci.build"
|
|
OperationTypeResourceProvision OperationType = "resource.provision"
|
|
)
|
|
|
|
// IsValid returns true if the operation type is known.
|
|
func (t OperationType) IsValid() bool {
|
|
switch t {
|
|
case OperationTypeProjectCreate, OperationTypeComponentAdd,
|
|
OperationTypeBuild, OperationTypeCIBuild, OperationTypeResourceProvision:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// OperationStatus represents the status of an operation.
|
|
type OperationStatus string
|
|
|
|
const (
|
|
OperationStatusPending OperationStatus = "pending"
|
|
OperationStatusRunning OperationStatus = "running"
|
|
OperationStatusCompleted OperationStatus = "completed"
|
|
OperationStatusFailed OperationStatus = "failed"
|
|
)
|
|
|
|
// IsValid returns true if the status is known.
|
|
func (s OperationStatus) IsValid() bool {
|
|
switch s {
|
|
case OperationStatusPending, OperationStatusRunning,
|
|
OperationStatusCompleted, OperationStatusFailed:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsTerminal returns true if the status is a final state.
|
|
func (s OperationStatus) IsTerminal() bool {
|
|
return s == OperationStatusCompleted || s == OperationStatusFailed
|
|
}
|
|
|
|
// OperationStep represents a single step within an operation.
|
|
type OperationStep struct {
|
|
// Name is the step identifier (e.g., "git", "build-api", "deploy-web").
|
|
Name string `json:"name"`
|
|
|
|
// Status is the step status.
|
|
Status OperationStatus `json:"status"`
|
|
|
|
// StartedAt is when the step started.
|
|
StartedAt time.Time `json:"started_at"`
|
|
|
|
// DurationMs is the step duration in milliseconds.
|
|
DurationMs int64 `json:"duration_ms,omitempty"`
|
|
|
|
// Output contains step-specific output data.
|
|
Output map[string]any `json:"output,omitempty"`
|
|
|
|
// Error is a one-line error summary.
|
|
Error string `json:"error,omitempty"`
|
|
|
|
// ErrorDetail is the full error detail.
|
|
ErrorDetail string `json:"error_detail,omitempty"`
|
|
}
|
|
|
|
// Operation represents a tracked project operation.
|
|
type Operation struct {
|
|
// ID is the unique operation identifier.
|
|
ID string `json:"id"`
|
|
|
|
// ProjectID is the project this operation belongs to.
|
|
ProjectID string `json:"project_id"`
|
|
|
|
// Type is the operation type.
|
|
Type OperationType `json:"type"`
|
|
|
|
// Status is the current operation status.
|
|
Status OperationStatus `json:"status"`
|
|
|
|
// RequestID is the HTTP request that initiated this operation.
|
|
RequestID string `json:"request_id,omitempty"`
|
|
|
|
// TriggeredBy is the ID of the parent operation that triggered this one.
|
|
TriggeredBy string `json:"triggered_by,omitempty"`
|
|
|
|
// CommitSHA is the git commit this operation created or was triggered by.
|
|
CommitSHA string `json:"commit_sha,omitempty"`
|
|
|
|
// ExternalRef is an external reference (e.g., "build#42").
|
|
ExternalRef string `json:"external_ref,omitempty"`
|
|
|
|
// StartedAt is when the operation started.
|
|
StartedAt time.Time `json:"started_at"`
|
|
|
|
// CompletedAt is when the operation finished.
|
|
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
|
|
|
// DurationMs is the total operation duration in milliseconds.
|
|
DurationMs int64 `json:"duration_ms,omitempty"`
|
|
|
|
// Input contains the operation input parameters.
|
|
Input map[string]any `json:"input,omitempty"`
|
|
|
|
// Output contains the operation output/result.
|
|
Output map[string]any `json:"output,omitempty"`
|
|
|
|
// Error is a one-line error summary.
|
|
Error string `json:"error,omitempty"`
|
|
|
|
// ErrorDetail is the full error detail (truncated to 10KB).
|
|
ErrorDetail string `json:"error_detail,omitempty"`
|
|
|
|
// Steps contains the operation steps.
|
|
Steps []OperationStep `json:"steps,omitempty"`
|
|
|
|
// CreatedAt is when the record was created.
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// StepsSummary returns a human-readable summary of step statuses.
|
|
// Example: "git ✓ → build-web ✓ → build-api ✗"
|
|
func (o *Operation) StepsSummary() string {
|
|
if len(o.Steps) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var parts []string
|
|
for _, step := range o.Steps {
|
|
symbol := "?"
|
|
switch step.Status {
|
|
case OperationStatusCompleted:
|
|
symbol = "✓"
|
|
case OperationStatusFailed:
|
|
symbol = "✗"
|
|
case OperationStatusRunning:
|
|
symbol = "…"
|
|
case OperationStatusPending:
|
|
symbol = "○"
|
|
}
|
|
parts = append(parts, step.Name+" "+symbol)
|
|
}
|
|
return strings.Join(parts, " → ")
|
|
}
|
|
|
|
// FailedStep returns the first failed step, or nil if none failed.
|
|
func (o *Operation) FailedStep() *OperationStep {
|
|
for i := range o.Steps {
|
|
if o.Steps[i].Status == OperationStatusFailed {
|
|
return &o.Steps[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OperationFilters specifies criteria for listing operations.
|
|
type OperationFilters struct {
|
|
// ProjectID filters by project (required for List).
|
|
ProjectID string
|
|
|
|
// Type filters by operation type.
|
|
Type OperationType
|
|
|
|
// Status filters by operation status.
|
|
Status OperationStatus
|
|
|
|
// CommitSHA filters by commit SHA.
|
|
CommitSHA string
|
|
|
|
// Since filters operations started after this time.
|
|
Since time.Time
|
|
|
|
// Limit is the maximum number of operations to return.
|
|
Limit int
|
|
}
|
|
|
|
// DefaultOperationFilters returns filters with default values.
|
|
func DefaultOperationFilters() OperationFilters {
|
|
return OperationFilters{
|
|
Limit: 50,
|
|
}
|
|
}
|
|
|
|
// Normalize applies defaults and limits to the filters.
|
|
func (f *OperationFilters) Normalize() {
|
|
if f.Limit <= 0 {
|
|
f.Limit = 50
|
|
}
|
|
if f.Limit > 200 {
|
|
f.Limit = 200
|
|
}
|
|
}
|
|
|
|
// MaxErrorDetailSize is the maximum size of error_detail (10KB).
|
|
const MaxErrorDetailSize = 10 * 1024
|
|
|
|
// TruncateErrorDetail truncates error detail to the maximum allowed size.
|
|
func TruncateErrorDetail(detail string) string {
|
|
if len(detail) <= MaxErrorDetailSize {
|
|
return detail
|
|
}
|
|
return detail[:MaxErrorDetailSize-3] + "..."
|
|
}
|