Documentation
¶
Overview ¶
Package matrix is the env × family screen: one row per family, one column per env, the family's image tag in each cell with its state spelled out — pinned, unpinned, split, external, drifted — as a word an operator can read without a legend. cells.go derives the cell values from a discovered repo (and, when the cluster has answered, from what each env is actually running) with no terminal dependency; model.go lays them out.
Index ¶
- func Detail(t Table, fam, env string, envIsProduction bool, inflight []flight.Summary) []string
- func FirstPartyRepos(fam *gitops.Family, promotable []string) []string
- func Grid(st ui.Styles, t Table, g GridState) []string
- type Cell
- type DriftFunc
- type DriftMsg
- type Focus
- type GridState
- type MenuItem
- type Model
- func (m Model) CapturesText() bool
- func (m Model) CurrentEnv() string
- func (m Model) CurrentFamily() string
- func (m Model) Cursor() int
- func (m Model) InFlight() []flight.Summary
- func (m Model) Init() tea.Cmd
- func (m Model) IsProduction(env string) bool
- func (m Model) KeyScreen() keys.Screen
- func (m Model) Matrix() Table
- func (m Model) Repo() *gitops.Repo
- func (m Model) RequestRefresh() (Model, tea.Cmd)
- func (m Model) SetInFlight(list []flight.Summary, err error) Model
- func (m Model) SetSize(width, height int) Model
- func (m Model) SetStyles(s ui.Styles) Model
- func (m Model) TakeRefreshError() (string, Model)
- func (m Model) Update(msg tea.Msg) (Model, tea.Cmd)
- func (m Model) View() string
- func (m Model) WithDrift(drift DriftFunc) Model
- func (m Model) WithNotice(notice string) Model
- func (m Model) WithNow(now func() time.Time) Model
- func (m Model) WithRefreshRepo(refresh RefreshRepoFunc) Model
- func (m Model) WithRepo(repo *gitops.Repo) Model
- func (m Model) WithRun(base, kubeContext string) Model
- type OpenConfigMsg
- type OpenPlanMsg
- type OpenRestartMsg
- type OpenTagsMsg
- type OpenWatchMsg
- type RefreshRepoFunc
- type RepoRefreshedMsg
- type ResumeMsg
- type Row
- type Running
- type State
- type Table
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Detail ¶ added in v0.2.0
Detail is the cell's own story: family, env, what the manifest says, what state word that earned, the drift sentence when applicable, whether the env is production, and — matched on Target only, the same coarse correlation MenuFor uses, since flight.Summary carries no family — any in-flight promotion into this env.
func FirstPartyRepos ¶
FirstPartyRepos lists, sorted, the distinct first-party image repos a family runs in one env — what d offers to deploy: one opens the picker directly, several open a chooser first.
func Grid ¶ added in v0.2.0
Grid renders the table's own bordered block: a header-divider rule, the header row (one cell per Table.Envs entry, already carrying whatever cursor/production marker the caller baked into the display copy of t — Grid itself knows nothing about config or which column is "selected" beyond g.Col, which only ever drives the CELL highlight), a header/body divider, then g.Height data rows starting at g.Offset (blank-padded if the table has fewer rows than that). It returns the block as a slice of lines rather than a joined string so a caller composing it into a larger section (the matrix's own View) can measure it with lipgloss.Height the same way every other section is measured.
Types ¶
type Cell ¶
type Cell struct {
// Present is false when the family has no Application in this env; the other fields
// are then zero and String renders blank.
Present bool
// Text is the tag shown: the single first-party tag, "v1/v2" when one build is pinned
// under several tags (sorted, so the text does not depend on file order), "N images"
// when the family's first-party occurrences span N image repos, or "N versions" when the
// one image repo runs under N builds inside the env (see builds). A third-party-only
// family shows the same for its third-party images.
Text string
// State is the word beside Text; "" for a present cell with no images at all. The split
// word is judged per image repo, so a "N images" cell is split whenever any one of its
// repos runs under two builds, and a "N versions" cell is always split: Text counts repos
// or builds, State says whether any repo's builds disagree, and both come from builds.
State State
// Pinned: every first-party occurrence carries a digest.
Pinned bool
// Differs: the set of refs differs from the previous env column's. Never set on the
// first column or when the previous column is blank. Kept as data for a caller that
// wants to colour a change across columns; it is no longer a glyph.
Differs bool
// ThirdParty: the family has no first-party image at all.
ThirdParty bool
// Running is what the cluster reports for a drifted cell, for the sentence under the
// table ("marketing runs sha-77c0ffe here; manifest says …"): the one running build's
// tag or short digest, or "N builds running (a, b)" when the pods run several. Builds
// are counted by digest where the pod reported one (else by tag), and two builds under
// the same tag are each named "tag · <short digest>" so they can be told apart — the
// case a mutable tag makes most likely. Empty unless State is StateDrifted.
Running string
// Compared names the comparison that decided the drift — "by digest" when any manifest
// reference for the repo is pinned and was compared with the undeclared build's digest,
// "by tag" only when every comparison made for that build was against a bare manifest
// reference — so the sentence says the strongest evidence it has. Empty unless State is
// StateDrifted.
Compared string
// contains filtered or unexported fields
}
Cell is one family × env intersection.
type DriftFunc ¶
DriftFunc reports what one env's cluster is running — the raw pod observations, keyed by canonical image repo, every distinct running reference kept (see Running) — asked for per env at boot so the matrix can say "drifted" where the manifest and the cluster disagree. It is not the planning resolver, which picks one digest per repo and would collapse a partial rollout (#122). nil means no cluster is configured and no cell is ever claimed drifted (nor not drifted). cmd/hoist builds it over pkg/k8s (AGENTS.md §4.8: this package takes a function value, never an adaptor).
type DriftMsg ¶
type DriftMsg struct {
// contains filtered or unexported fields
}
DriftMsg is one env's answer. from is the scope ID of the refresh it belongs to (scope.New at construction and again on each refresh), so a slow answer from before a refresh (F5) or a previous instance of this screen is dropped rather than painting stale drift over a newer answer.
type Focus ¶ added in v0.2.0
type Focus uint8
Focus says which part of the matrix screen currently owns the cursor keys (tab moves it, the screen's own new binding): the grid itself, or the in-flight pane underneath it.
type GridState ¶ added in v0.2.0
GridState is the grid renderer's own input beyond the Table text itself (the audit doc's shape): the cell cursor (Row, Col — Col is an index into Widths' env columns, i.e. Table.Envs, not into Widths itself, which carries one extra leading entry for FAMILY), the vertical scroll Offset and how many data rows fit (Height), each column's fitted width (Widths[0] is FAMILY; Widths[i+1] is Table.Envs[i]), and which side of the screen currently has the cursor (Focus) — the grid dims nothing based on Focus itself; a screen that wants to show the pane focused draws that separately. Cursor cell styling instead only applies to the exact (Row, Col) intersection when Focus is FocusGrid, so tabbing to the pane visibly moves the highlight off the table rather than leaving a stale cell lit.
type MenuItem ¶ added in v0.2.0
MenuItem is one row of the action menu: a Binding when the item has its own letter (so a keypress can run it directly without moving the cursor first), the label shown beside it, the message running it emits, and whether the cursor's own cell actually supports it right now (a disabled item is still listed — v2·02 shows disabled writes on a production column with an "(asks)"-style constraint stated once, never a hidden action the operator wonders where it went, AGENTS.md principle 5's own "warn, don't block").
func MenuFor ¶ added in v0.2.0
func MenuFor(t Table, fam, env string, envs config.EnvsConfig, inflight []flight.Summary) []MenuItem
MenuFor lists what enter can do for the cell (fam, env): promote into env, deploy a tag, watch, restart, plus one row per in-flight promotion already targeting env — matched on Target alone, since flight.Summary carries no family (a promotion targets an env, not one row of it), which is this function's own known imprecision when two families are promoting into the same env at once (ticketed rather than chased here).
type Model ¶
type Model struct {
// contains filtered or unexported fields
}
Model is the matrix screen. It is a value: Update, SetSize and SetStyles return the updated model.
func New ¶
New builds the screen for a discovered repo. promotable lists the first-party image repo prefixes (see Compute). envs is the repo's policy. drift is how the cluster is asked what each env runs; nil never asks. Columns come in pipeline order (envs.pairs) rather than alphabetically, and the cell cursor starts on the first non-production column so a fresh session never opens with the cursor already pointed at a write that asks for approval. The model has no size until SetSize is called.
func (Model) CapturesText ¶
CapturesText reports whether some dialog is open that takes letters as text — the image chooser's "/" filter, or its own confirm dialogs — and the root's q-to-quit must not fire over it. The action menu counts: it runs an item on a letter, so the root's q, l and ? must reach the menu (which ignores them) rather than quit, push the log or open help over it (UX-H13).
func (Model) CurrentEnv ¶
CurrentEnv is the env the column cursor is on, "" when the repo has none.
func (Model) CurrentFamily ¶
CurrentFamily is the family the row cursor is on, "" when the matrix has no rows.
func (Model) Init ¶
Init asks the cluster what every env is running, one command per env, when a DriftFunc was supplied.
func (Model) IsProduction ¶
IsProduction reports whether env is listed in envs.production.
func (Model) KeyScreen ¶ added in v0.2.0
KeyScreen implements the root's keyed interface: the matrix now has a stated row in internal/ui/keys' registry, so the root's own "?" help overlay and "l" activity-log handling apply to it exactly as they already do to watch/restart/config/activity.
func (Model) Repo ¶ added in v0.2.0
Repo returns the *gitops.Repo the table is currently computed from.
func (Model) RequestRefresh ¶ added in v0.2.0
RequestRefresh is the completion-triggered refresh: exactly what F5 already does, exported so the root can call it when a drive lands or finishes.
func (Model) SetInFlight ¶
SetInFlight replaces what the pane shows. err is the listing's own failure (the state directory unreadable), shown in the pane's place; a per-promotion re-observation failure travels inside its Summary.
func (Model) TakeRefreshError ¶ added in v0.2.0
TakeRefreshError returns and clears a failed F5's own reason, so the root can log it to the activity log instead of leaving it as the matrix's own clear-on-next-key notice — an async refresh's failure can easily land after the operator has already pressed another key.
func (Model) View ¶
View renders the frame: the subheader, the grid, the notes, the in-flight pane (inside the frame) and the footer, with the menu or a chooser dialog over it when one is open.
func (Model) WithNotice ¶
WithNotice sets the notice shown under the table.
func (Model) WithNow ¶
WithNow fixes the clock the pane ages promotions with (tests); the default is time.Now.
func (Model) WithRefreshRepo ¶ added in v0.2.0
func (m Model) WithRefreshRepo(refresh RefreshRepoFunc) Model
WithRefreshRepo installs the function F5 calls to re-read the repo.
type OpenConfigMsg ¶ added in v0.2.0
type OpenConfigMsg struct{}
OpenConfigMsg is emitted when the operator asks to read the effective config (c, #104): the TUI's `hoist config show`. It carries nothing — the root holds the text and the path.
type OpenPlanMsg ¶
type OpenPlanMsg struct {
Source, Target string
}
OpenPlanMsg is emitted when the operator asks to plan a promotion into Target (p, or the action menu's own "promote into" item). Source is the one reverse pair (envs.pairs) found for Target when exactly one exists; empty when there is none or several, in which case the plan screen itself asks "promote into Target from…" (promote now always goes by the TARGET the cursor is on, never a forced prompt for an arbitrary target — the retired P key's own job).
type OpenRestartMsg ¶
type OpenRestartMsg struct {
Family, Target string
}
OpenRestartMsg is emitted when the operator asks to restart the family under the cursor in CurrentEnv (shift+r).
type OpenTagsMsg ¶
type OpenTagsMsg struct {
ImageRepo, Target string
}
OpenTagsMsg is emitted when the operator asks to pick a new tag for the current cell (t, "deploy a tag" — the tag picker, internal/app/tags). ImageRepo is the one first-party image repo the family runs in CurrentEnv, or the one the operator chose when there were several.
type OpenWatchMsg ¶ added in v0.2.0
type OpenWatchMsg struct {
Family, Target string
}
OpenWatchMsg is emitted when the operator asks to watch the family under the cursor converge in CurrentEnv (w).
type RefreshRepoFunc ¶ added in v0.2.0
RefreshRepoFunc re-reads the repo this matrix shows — cmd/hoist's own fetch-then-discover against a cached origin/<base> view (#PR7), never the operator's own working tree (AGENTS.md §4.6). Mirrors DriftFunc's own shape: a plain function type this screen holds and calls (§4.8 — a screen may call a function type it's handed, just never import the git/network machinery behind it itself), built once by cmd/hoist and installed with WithRefreshRepo. The returned rev is origin/<base>'s tip at the moment this view was built (svc.RefreshRepo's own RepoView.SHA, #PR8) — "" when the fetch fell back to the clone, since there is then nothing freshly read to name.
type RepoRefreshedMsg ¶ added in v0.2.0
RepoRefreshedMsg carries RefreshRepoFunc's answer back to Update. Gen is the scope ID of the askRepoRefresh call that issued it, the same way DriftMsg's own from does for the cluster fan-out. A Model that has not yet asked holds the zero ID, which is what an unstamped Gen of 0 matches.
func (RepoRefreshedMsg) StampedBy ¶ added in v0.3.0
func (r RepoRefreshedMsg) StampedBy() scope.ID
StampedBy implements scope.Stamped.
type ResumeMsg ¶
ResumeMsg asks the root to re-drive one in-flight promotion on the flight screen (r, or enter on the pane) — the TUI's `hoist resume <id>`. Build carries the session.Controller BuildID for an entry still Building: ID is empty until a real promotion id exists, so re-attaching to it before then has nothing else to key by — the root re-attaches by Build directly (session.Controller.BuildSnapshot) rather than calling Resume with an empty id, which would fail to find anything and, worse, would have started a second drive had Resume("") happened to succeed.
type Running ¶
Running is what each env's cluster is actually running, keyed env → image repo (in the registry's canonical spelling, image.Canonical) → every distinct running reference (the digest each pod reports, and the tag the pod was started from when it reported one). Every build is kept: a partial rollout is two entries, never the one a resolver would choose (#122). nil, or a missing env, means the cluster has not answered for that env — no cell there can be drifted, and none is claimed not to be.
type State ¶
type State string
State is the one word a cell shows beside its tag. Colour reinforces it; it never replaces it (docs/tui/mockups.html: "state is a word, not a glyph").
const ( // StatePinned means every first-party occurrence carries a digest. StatePinned State = "pinned" // StateUnpinned means at least one first-party occurrence is a bare tag — a moved tag is // invisible to imagePullPolicy: IfNotPresent (AGENTS.md principle 3). StateUnpinned State = "unpinned" // StateSplit means one image repo runs under more than one build inside the env (two // digests, or two tags — see builds) — the condition the runbook blocks a promotion on, // so it should look like a problem. Judged per repo, so a multi-repo cell can be split too. StateSplit State = "split" // StateExternal means the family has no first-party image at all. StateExternal State = "external" // StateDrifted means the cluster runs a build the manifest does not declare. The single most // important fact on the screen when it is true, so it gets a sentence under the table too. StateDrifted State = "drifted" )
type Table ¶
Table is the whole matrix: envs sorted by name, families sorted by name (the union across envs).
func Compute ¶
Compute derives the matrix from a discovered repo. promotable lists the image repo prefixes that count as first-party (what hoist plan --promotable takes); an occurrence matching none is third-party. running may be nil.
func Order ¶ added in v0.2.0
func Order(t Table, envs config.EnvsConfig) Table
Order reorders t's columns into the pipeline order envs.PipelineOrder derives from envs.pairs (UX-M9's own header-order companion): a promotion flows source-to-target, so the matrix should read that way left-to-right instead of alphabetically. Pure: every Row's Cells is permuted along with Envs so index i in the result still means the same env for both, and no cell's own content is touched.