Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RenderedLineCount ¶
RenderedLineCount returns the number of terminal rows the rendered block currently occupies, accounting for line wrapping at the detected terminal width. Use it as the argument to "\033[<N>F" (cursor up + clear) so the wipe lands exactly on the start of what was printed last time, even after the operator has resized (e.g. tmux split, font-size change, full-window resize) mid-render.
`strings.Count(block, "\n")` alone is wrong on resize: the count is the number of logical newlines in the printed string, but the terminal may have wrapped each long line to 2+ rows. Cursor-up by the logical count then lands inside the previous block — the next render appends below it, accumulating ghost headers each tick (visible signature: empty "Resource | Phase | Status" rows stacking under a footer that keeps moving).
Width detection falls back to logical-newline counting when stdout isn't a terminal or term.GetSize fails — same behaviour as before in non-interactive contexts (CI logs, redirected output).
Types ¶
type Bar ¶
type Bar struct {
// contains filtered or unexported fields
}
Bar is a section-style progress logger. The transcript reads as:
● Provisioning Hetzner infrastructure ───────────────────────────────────── ✓ Created Hetzner Network ✓ Registered HCloud SSH key ✓ Created control-plane Load Balancer ⠴ [12s]
Each major step opens a section: bold "● <step>" header on its own line, then a horizontal rule the same width as the header. Substeps stream below in faint " ✓ <work>" rows as they complete. The bar's spinner ticks on the line below the section to telegraph "still in the middle of this section".
A zero-value Bar{} (returned by FromCtx for contexts with no bar attached) is silently a no-op — every method nil-guards so test code and library callers don't have to care.
func FromCtx ¶
FromCtx returns the *Bar attached to ctx via WithBar, or a no-op Bar when none is attached. Callers can therefore always do `progress.FromCtx(ctx).Substep(...)` without nil-checking.
func New ¶
New creates a spinner-style progress bar (unknown length) with the given header. The header is the whole-run label; it does NOT get a "✓" line of its own — the first Describe call starts the first real step.
func (*Bar) Describe ¶
Describe opens a new major-step section. Prints a blank line (when transitioning from a previous section), the bold "● <step>" header, and a horizontal rule whose width matches the header text. Substeps that follow stream below in faint " ✓ <work>" rows.
Top-to-bottom the final transcript reads as:
● Provisioning Hetzner infrastructure ───────────────────────────────────── ✓ Created Hetzner Network ✓ Created NAT Gateway ● Creating management cluster ────────────────────────────── ✓ Cloned kubeaid-config repo ⠴ [12s]
No-op for repeat Describe calls with the same description.
func (*Bar) Finish ¶
func (b *Bar) Finish()
Finish clears the spinner. Substeps are already a flat list under the section header so there's no tree branch to close — the next major-step section's header serves as the implicit boundary.
func (*Bar) InProgress ¶
InProgress emits a transient " ↻ <text>" sub-step under the active major-step header for a long-running step that would otherwise look like silent dead time on the spinner — ArgoCD app sync (~30s+ each), Helm install of Sealed Secrets / ArgoCD, kubeaid-config render, etc. The release closure ERASES the line once the step completes; the caller follows up with a permanent "✓ <text>" via Substep so the audit trail still shows the finished work.
Pair via `defer bar.InProgress("Syncing X")()` around the long-running call. Same caveat as RequestYubiKeyTouch: the erase assumes no other Substep / InProgress / RequestYubiKeyTouch fires between Request and the closure call, so bracket as tightly as possible.
func (*Bar) Pause ¶
func (b *Bar) Pause()
Pause silences the bar's writes to stderr — including the spinner's 100ms auto-render goroutine inside progressbar/v3. Use around interactive stdin prompts so the spinner can't overwrite the prompt line via its `\r`-anchored re-render. Internal state (counters, elapsed time) keeps updating; only the visible output is suppressed.
Pair with Resume. Calling Pause clears the spinner line via a direct stderr write so the prompt has a clean line to print into.
func (*Bar) RequestYubiKeyTouch ¶
RequestYubiKeyTouch emits a transient "👉 Tap YubiKey to <reason>" sub-step while the spinner is paused on a hardware-touch SSH signature, then ERASES that line once the SSH op completes — the work sub-step that follows ("Cloned X", "Pushed Y") is the audit trail. We don't keep a permanent "Touched ✓" line because a single major step often does several SSH ops back-to-back; a stack of identical "Touched ✓" lines would crowd out the real progress without adding info.
reason names what the operator is authorizing — "clone repo", "push branch", "fetch updates" — and shows up in the prompt so they know what they're approving. Keep it short (a few words); the prompt is one substep line and shouldn't wrap.
Pair via `defer bar.RequestYubiKeyTouch("...")()` around the actual SSH op. Caveat: the erase assumes no other Substep calls fire between Request and the closure call, so bracket as tightly as possible around the op — emitting other sub-steps in between will cause the erase to target the wrong line.
No-op (returns a no-op closure) when no YubiKey-backed identity is loaded in the SSH agent — software-only agents never block for a hardware touch. Card detection is cached at Bar construction; plugging in the YubiKey mid-bootstrap won't be picked up until next run.
func (*Bar) Resume ¶
func (b *Bar) Resume()
Resume re-enables the bar's writes. The spinner re-appears on the next render (within ~100ms via the auto-tick goroutine, or sooner if a Substep/Describe call triggers a render).
func (*Bar) Substep ¶
Substep prints " ✓ <text>" in faint style under the active major- step header. Substeps stream below the section's underline as work completes; the bar's spinner re-renders below them on its next tick.
Faint styling is applied immediately rather than retroactively (each substep is the audit trail of finished work — there's no "active substep" concept; the bar's spinner is the active surface). Operator's eye scans past the dim list and lands on the spinner.