ciwait

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package ciwait waits for the CI jobs that make a chart installable, and shows live progress while it does.

The package is split deliberately: Machine is a pure state machine over job payloads, with no network and no terminal, and the renderer is separate. That is what makes the percentage-monotonicity and job-matching rules testable as table-driven cases.

Index

Constants

View Source
const (
	MinPollInterval     = 5 * time.Second
	DefaultPollInterval = 10 * time.Second
	QueuedPollInterval  = 15 * time.Second
	MaxPollInterval     = 30 * time.Second
	DefaultWaitTimeout  = 45 * time.Minute
)

Polling intervals. The floor is deliberate: polling faster than 5s burns rate limit without telling the user anything new.

View Source
const RateLimitFloor = 100

RateLimitFloor is the remaining-request budget below which the wait widens its poll interval rather than spending what is left of the quota.

Variables

This section is empty.

Functions

func FormatDuration

func FormatDuration(d time.Duration) string

FormatDuration renders a duration the way the progress display shows it: "41s", "2m03s", "1h04m".

Types

type Display

type Display interface {
	Update(State)
	Retarget(Retarget)
	Note(string)
	// Close tears the display down, leaving the terminal usable.
	Close()
}

Display shows a wait in progress. Three implementations exist: a live bubbletea view for terminals, a transition-only line log for pipes and --no-color, and a silent one for --json.

func NewDisplay

func NewDisplay(r *output.Renderer, target Target, chart string) Display

NewDisplay picks the right implementation for the current output mode.

type ErrCancelled

type ErrCancelled struct{ Target Target }

ErrCancelled reports a cancelled run with no successor to retarget onto.

func (*ErrCancelled) Error

func (e *ErrCancelled) Error() string

type ErrJobFailed

type ErrJobFailed struct {
	Job  string
	Step string
	URL  string
}

ErrJobFailed reports a gating job that failed.

func (*ErrJobFailed) Error

func (e *ErrJobFailed) Error() string

type ErrNoRun

type ErrNoRun struct {
	Branch   string
	Workflow string
}

ErrNoRun reports a branch with no workflow runs. This is a normal state for an unpushed branch, and the message says exactly what to do about it.

func (*ErrNoRun) Error

func (e *ErrNoRun) Error() string

type ErrTimeout

type ErrTimeout struct {
	Target  Target
	Elapsed time.Duration
}

ErrTimeout reports that the wait exceeded its budget.

func (*ErrTimeout) Error

func (e *ErrTimeout) Error() string

type Event

type Event struct {
	// State is a new progress snapshot.
	State *State
	// Retargeted reports that the watched run was replaced.
	Retargeted *Retarget
	// Note carries a one-off message, such as a rate-limit warning.
	Note string
	// Err is a terminal error.
	Err error
}

Event is one update from the wait loop. Exactly one of the fields is set.

type JobView

type JobView struct {
	Name string `json:"name"`
	// Description is never just a state: it names the step currently running,
	// or how the job finished.
	Description string           `json:"description"`
	Status      ghapi.RunStatus  `json:"status"`
	Conclusion  ghapi.Conclusion `json:"conclusion,omitempty"`
	Duration    time.Duration    `json:"-"`
	DurationStr string           `json:"duration,omitempty"`
	HTMLURL     string           `json:"html_url,omitempty"`
	// Gating reports whether this job is one the wait depends on.
	Gating bool `json:"gating"`
}

JobView is one row of the progress display.

func (JobView) Skipped

func (j JobView) Skipped() bool

Skipped reports a job GitHub chose not to run.

type Machine

type Machine struct {
	// Targets are job-name prefixes that gate the wait. Empty means every job
	// in the run gates, which is the fallback when a repo configures none.
	Targets []string
	// IgnoreFailures keeps waiting when a gating job fails, for the case where
	// a non-essential matrix leg failed but the chart published anyway.
	IgnoreFailures bool
	// contains filtered or unexported fields
}

Machine turns successive job listings into display state. It holds exactly one piece of history — the highest percentage seen — because the percentage must never go backwards while the user watches it.

func NewMachine

func NewMachine(targets []string) *Machine

NewMachine returns a Machine for a set of gating job-name prefixes.

func (*Machine) Retarget

func (m *Machine) Retarget()

Retarget resets the progress history. This is the one sanctioned way for the percentage to decrease: the run being watched was replaced.

func (*Machine) Update

func (m *Machine) Update(jobs []ghapi.Job) State

Update folds a job listing into display state.

type Options

type Options struct {
	Owner        string
	Repo         string
	WorkflowFile string
	Branch       string
	// RunID pins the run to watch. Zero means "find the newest run".
	RunID int64
	// WaitForJobs are the gating job-name prefixes.
	WaitForJobs []string

	PollInterval   time.Duration
	Timeout        time.Duration
	IgnoreFailures bool
}

Options configure a wait.

type Phase

type Phase int

Phase is the overall state of a wait.

const (
	// PhaseWaiting means gating jobs are still running or queued.
	PhaseWaiting Phase = iota
	// PhaseSucceeded means every gating job finished satisfactorily.
	PhaseSucceeded
	// PhaseFailed means a gating job failed.
	PhaseFailed
	// PhaseCancelled means the watched run was cancelled, usually by a newer push.
	PhaseCancelled
)

Wait phases.

func (Phase) String

func (p Phase) String() string

String returns the phase name used in output.

type Result

type Result struct {
	Target  Target        `json:"target"`
	Jobs    []JobView     `json:"jobs"`
	Elapsed time.Duration `json:"-"`
	Total   string        `json:"total_wait,omitempty"`
}

Result is the outcome of a completed wait.

type Retarget

type Retarget struct {
	From Target
	To   Target
	// Reason explains the switch, for display.
	Reason string
}

Retarget records a switch to a different run or attempt.

type State

type State struct {
	Phase Phase `json:"phase"`
	// Jobs holds the gating jobs, in the configured order.
	Jobs []JobView `json:"jobs"`
	// Percent is monotonic across a run: it never decreases except on a
	// deliberate retarget, which resets it to zero.
	Percent float64 `json:"percent"`

	// FailedJob, FailedStep and FailedURL are set when Phase is PhaseFailed.
	FailedJob  string `json:"failed_job,omitempty"`
	FailedStep string `json:"failed_step,omitempty"`
	FailedURL  string `json:"failed_url,omitempty"`

	// Matched reports how many jobs matched the gating patterns. Zero means CI
	// has not created them yet, which is a normal early state.
	Matched int `json:"matched"`
}

State is the machine's output after one poll.

type Target

type Target struct {
	RunID   int64  `json:"run_id"`
	Attempt int    `json:"run_attempt,omitempty"`
	URL     string `json:"url,omitempty"`
	Name    string `json:"workflow,omitempty"`
	Branch  string `json:"branch,omitempty"`
}

Target identifies the run currently being watched.

type Waiter

type Waiter struct {
	API  ghapi.API
	Opts Options
	// contains filtered or unexported fields
}

Waiter polls GitHub and emits progress events.

func NewWaiter

func NewWaiter(api ghapi.API, opts Options) *Waiter

NewWaiter builds a Waiter.

func (*Waiter) ETA

func (w *Waiter) ETA(ctx context.Context, elapsed time.Duration) (time.Duration, bool)

ETA estimates the remaining time from the median of recent successful runs. It returns false rather than a wild guess when there is no history.

func (*Waiter) Resolve

func (w *Waiter) Resolve(ctx context.Context) (Target, error)

Resolve finds the run to watch: the pinned one, or the newest on the branch.

func (*Waiter) Run

func (w *Waiter) Run(ctx context.Context, events chan<- Event) (Result, error)

Run polls until the gating jobs finish, emitting an Event on every poll. The channel is closed when the wait ends; the final event carries either Err or a terminal State.

SIGINT is handled by the caller cancelling ctx: the loop returns context.Canceled promptly, so the renderer can restore the terminal.

func (*Waiter) Target

func (w *Waiter) Target() Target

Target reports the run currently being watched.

Jump to

Keyboard shortcuts

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