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 ¶
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.
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 ¶
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.
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 ¶
ErrJobFailed reports a gating job that failed.
func (*ErrJobFailed) Error ¶
func (e *ErrJobFailed) Error() string
type ErrNoRun ¶
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.
type ErrTimeout ¶
type ErrTimeout struct {
Target Target
Elapsed time.Duration
// Jobs is the last gating-job snapshot seen before the deadline. A timeout
// used to discard it, which loses the view exactly when it matters most:
// knowing *which* job was still running is the whole reason to look.
Jobs []JobView
}
ErrTimeout reports that the wait exceeded its budget.
func (*ErrTimeout) Error ¶
func (e *ErrTimeout) Error() string
func (*ErrTimeout) Outstanding ¶ added in v0.2.0
func (e *ErrTimeout) Outstanding() []string
Outstanding names the gating jobs that had not finished.
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.
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 ¶
NewMachine returns a Machine for a set of gating job-name prefixes.
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
// PollIntervalSet reports that the user named PollInterval themselves. An
// explicit value is a floor: the adaptive rate may still slow polling
// down, but it must not speed it past what was asked for. Left false, the
// default interval adapts freely in both directions.
PollIntervalSet bool
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.
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 ¶
Waiter polls GitHub and emits progress events.
func (*Waiter) ETA ¶
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 ¶
Resolve finds the run to watch: the pinned one, or the newest on the branch.
func (*Waiter) Run ¶
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.