runs

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package runs persists run state to a JSON file with flock protection.

Index

Constants

This section is empty.

Variables

View Source
var ErrRunNotFound = errors.New("run not found")

ErrRunNotFound is returned by Get / Update when the requested run ID does not exist in the store. It is a sentinel so callers can branch with errors.Is.

Functions

This section is empty.

Types

type PrepareOutcome

type PrepareOutcome int

PrepareOutcome classifies the result of PrepareAtomic.

const (
	// PrepareCreated: a fresh pending run was created (returned run is new).
	PrepareCreated PrepareOutcome = iota
	// PrepareReuse: an existing non-terminal run with the same (machine,hash)
	// was found — idempotency hit (returned run is the existing one).
	PrepareReuse
	// PrepareBusy: the machine already has a non-terminal run for a DIFFERENT
	// hash (returned run is the occupying run).
	PrepareBusy
)

type Run

type Run struct {
	ID            string     `json:"id"`
	Machine       string     `json:"machine"`
	TestDir       string     `json:"test_dir"`
	TestDirHash   string     `json:"test_dir_hash"`
	Status        Status     `json:"status"`
	ExitCode      *int       `json:"exit_code"`
	StartedAt     *time.Time `json:"started_at"`
	EndedAt       *time.Time `json:"ended_at"`
	ArtifactsPath string     `json:"artifacts_path"`
	LogsPath      string     `json:"logs_path"`
	ErrorCode     string     `json:"error_code"`
	ErrorMsg      string     `json:"error_msg"`
	// BatchID is the owning batch's ID when this run was dispatched as part of a
	// batch; empty for single `run` dispatches. omitempty keeps v1 single-run
	// JSON byte-identical for existing consumers.
	BatchID string `json:"batch_id,omitempty"`
	// PlanEntry is the plan entry name this run fulfills within its batch; empty
	// for single dispatches.
	PlanEntry string `json:"plan_entry,omitempty"`
}

Run is the persisted record for one test dispatch.

JSON field names are snake_case to match the public CLI/JSON contract. Pointer fields (ExitCode, StartedAt, EndedAt) are nil while the run is still pending, becoming non-nil as the lifecycle progresses; they serialize as `null` then.

type Status

type Status string

Status is the lifecycle state of a Run.

String-backed so JSON serialization is human-readable and stable for the machine-readable contract. CLI tooling branches on these values.

const (
	StatusPending     Status = "pending"
	StatusRunning     Status = "running"
	StatusCompleted   Status = "completed"
	StatusFailed      Status = "failed"
	StatusInterrupted Status = "interrupted"
	StatusTimeout     Status = "timeout"
)

func (Status) IsTerminal

func (s Status) IsTerminal() bool

IsTerminal returns true when the run has reached a final state and will not transition further. Idempotency in the runner layer keys off this distinction: non-terminal (pending/running) re-dispatches dedupe; terminal re-dispatches create a fresh run.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store persists runs to a JSON file with flock-based cross-process locking.

The store is safe for concurrent use within a process (mu) and across processes (flock). Every public method takes the in-proc mutex and then acquires/releases the on-disk lock; this keeps the critical section bounded and avoids fd leaks.

func NewStore

func NewStore(stateDir string) (*Store, error)

NewStore prepares the state directory and returns a Store rooted at it. The directory must already be createable (caller typically uses config.Config.StateDir which mkdir -p's with 0700).

func (*Store) ActiveByMachine

func (s *Store) ActiveByMachine(name string) (*Run, bool)

ActiveByMachine returns the first run on the given machine whose status is pending or running. The bool is false when no such run exists.

MVP assumes a single active run per machine (enforced at the runner layer via machine_busy); if multiple ever exist, the earliest-inserted wins.

func (*Store) Create

func (s *Store) Create(r *Run) error

Create assigns a unique ID to r (overwriting any existing value) and appends it to the store.

func (*Store) FindByTestDirHashAndMachine

func (s *Store) FindByTestDirHashAndMachine(hash, machine string) ([]*Run, error)

FindByTestDirHashAndMachine returns every run that matches (hash, machine).

NOTE: the runner's preflight no longer calls this — the idempotency + busy check + Create now happen atomically inside PrepareAtomic (issue #10). This remains a standalone query for callers that want the full match set.

func (*Store) Get

func (s *Store) Get(id string) (*Run, error)

Get returns the run with the given ID, or ErrRunNotFound.

func (*Store) List

func (s *Store) List() ([]*Run, error)

List returns all runs (in insertion order).

func (*Store) PrepareAtomic

func (s *Store) PrepareAtomic(machine, hash string, mk func() *Run) (*Run, PrepareOutcome, error)

PrepareAtomic performs the machine-busy check, the idempotency check, and the pending-run Create in a SINGLE critical section. Two concurrent dispatchers (e.g. a foreground batch first-wave and a still-live detached worker, or two different batches targeting the same machine) can therefore never both create a run for the same machine — which previously left an orphan run unreferenced by any batch item (issue #10). mk is invoked only on the create path and must return the pending run to persist; its ID is assigned here.

This subsumes the older separate ActiveByMachine + FindByTestDirHashAndMachine sequence the runner used to call across distinct lock acquisitions: a non-terminal run on the machine is always observed here first (single-active- per-machine invariant), so a same-hash twin reuses and a different-hash run reports busy, all under one held lock.

func (*Store) Update

func (s *Store) Update(r *Run) error

Update overwrites the stored record matching r.ID. Returns ErrRunNotFound if no such record exists.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL