Module: Everywhere::UI
- Defined in:
- lib/everywhere/ui.rb
Overview
Terminal output helpers. Colors follow the informal standard: on when stdout is a TTY or CLICOLOR_FORCE=1, always off when NO_COLOR is set.
The hosted runner streams every release through a pipe (not a TTY) into the
Platform web log, so it exports CLICOLOR_FORCE=1 to keep our color — the web
UI renders ANSI. That's why we force rather than sniff the TTY there.
Defined Under Namespace
Classes: Status
Constant Summary collapse
- OS_LABELS =
Display casing for os tokens. Target IDs ("macos-arm64") stay lowercase wherever they're typed or stored; use these only in human-facing output.
{ "macos" => "macOS", "ios" => "iOS", "android" => "Android", "windows" => "Windows", "linux" => "Linux" }.freeze
Class Method Summary collapse
- .bad(msg) ⇒ Object
- .bad_line(msg) ⇒ Object
- .blue(t) ⇒ Object
- .bold(t) ⇒ Object
- .color? ⇒ Boolean
- .cyan(t) ⇒ Object
- .detail(msg) ⇒ Object
- .detail_line(msg) ⇒ Object
- .die!(msg) ⇒ Object
- .dim(t) ⇒ Object
-
.elapsed(seconds) ⇒ Object
Human elapsed time — "48s", "2m 14s", "1h 3m".
-
.gray(t) ⇒ Object
bright black — dimmer than dim() on most themes.
- .green(t) ⇒ Object
- .magenta(t) ⇒ Object
- .note(msg, marker: "·", color: :gray) ⇒ Object
-
.note_line(msg, marker: "·", color: :gray) ⇒ Object
An indented, dim outcome under a substep — e.g.
- .ok(msg) ⇒ Object
- .ok_line(msg) ⇒ Object
- .os_label(os) ⇒ Object
- .paint(text, *codes) ⇒ Object
- .phase(msg) ⇒ Object
-
.phase_line(msg) ⇒ Object
--- output levels --------------------------------------------------------.
- .red(t) ⇒ Object
-
.short_path(str) ⇒ Object
Collapse the noisy machine paths that external tools echo (codesign, notarytool, stapler) down to something a human can scan.
- .step(msg) ⇒ Object
- .step_line(msg) ⇒ Object
- .substep(msg) ⇒ Object
- .substep_line(msg) ⇒ Object
- .success(msg) ⇒ Object
- .success_line(msg) ⇒ Object
-
.target_label(target) ⇒ Object
"macos-arm64" → "macOS-arm64".
- .warn(msg) ⇒ Object
- .warn_line(msg) ⇒ Object
- .yellow(t) ⇒ Object
-
.yn(bool) ⇒ Object
A yes/no fact, colored by truthiness (for signing receipts).
Class Method Details
.bad(msg) ⇒ Object
77 |
# File 'lib/everywhere/ui.rb', line 77 def bad(msg) = puts(bad_line(msg)) |
.bad_line(msg) ⇒ Object
68 |
# File 'lib/everywhere/ui.rb', line 68 def bad_line(msg) = "#{red("✗")} #{msg}" |
.blue(t) ⇒ Object
29 |
# File 'lib/everywhere/ui.rb', line 29 def blue(t) = paint(t, 34) |
.bold(t) ⇒ Object
24 |
# File 'lib/everywhere/ui.rb', line 24 def bold(t) = paint(t, 1) |
.color? ⇒ Boolean
13 14 15 16 17 18 |
# File 'lib/everywhere/ui.rb', line 13 def color? return false if ENV["NO_COLOR"] return true if ENV["CLICOLOR_FORCE"] == "1" $stdout.tty? end |
.cyan(t) ⇒ Object
31 |
# File 'lib/everywhere/ui.rb', line 31 def cyan(t) = paint(t, 36) |
.detail(msg) ⇒ Object
75 |
# File 'lib/everywhere/ui.rb', line 75 def detail(msg) = puts(detail_line(msg)) |
.detail_line(msg) ⇒ Object
66 |
# File 'lib/everywhere/ui.rb', line 66 def detail_line(msg) = " #{gray("·")} #{gray(msg)}" |
.die!(msg) ⇒ Object
89 90 91 |
# File 'lib/everywhere/ui.rb', line 89 def die!(msg) abort "#{red("✗")} #{red(msg)}" end |
.dim(t) ⇒ Object
25 |
# File 'lib/everywhere/ui.rb', line 25 def dim(t) = paint(t, 2) |
.elapsed(seconds) ⇒ Object
Human elapsed time — "48s", "2m 14s", "1h 3m".
152 153 154 155 156 157 158 |
# File 'lib/everywhere/ui.rb', line 152 def elapsed(seconds) secs = seconds.to_i return "#{secs}s" if secs < 60 return "#{secs / 60}m #{secs % 60}s" if secs < 3600 "#{secs / 3600}h #{(secs % 3600) / 60}m" end |
.gray(t) ⇒ Object
bright black — dimmer than dim() on most themes
32 |
# File 'lib/everywhere/ui.rb', line 32 def gray(t) = paint(t, 90) # bright black — dimmer than dim() on most themes |
.green(t) ⇒ Object
27 |
# File 'lib/everywhere/ui.rb', line 27 def green(t) = paint(t, 32) |
.magenta(t) ⇒ Object
30 |
# File 'lib/everywhere/ui.rb', line 30 def magenta(t) = paint(t, 35) |
.note(msg, marker: "·", color: :gray) ⇒ Object
87 |
# File 'lib/everywhere/ui.rb', line 87 def note(msg, marker: "·", color: :gray) = puts(note_line(msg, marker: marker, color: color)) |
.note_line(msg, marker: "·", color: :gray) ⇒ Object
An indented, dim outcome under a substep — e.g. "accepted", "valid on disk".
82 83 84 85 |
# File 'lib/everywhere/ui.rb', line 82 def note_line(msg, marker: "·", color: :gray) tint = respond_to?(color) ? method(color) : method(:gray) " #{tint.call(marker)} #{tint.call(msg)}" end |
.ok(msg) ⇒ Object
76 |
# File 'lib/everywhere/ui.rb', line 76 def ok(msg) = puts(ok_line(msg)) |
.ok_line(msg) ⇒ Object
67 |
# File 'lib/everywhere/ui.rb', line 67 def ok_line(msg) = "#{green("✓")} #{msg}" |
.os_label(os) ⇒ Object
44 |
# File 'lib/everywhere/ui.rb', line 44 def os_label(os) = OS_LABELS.fetch(os.to_s, os.to_s) |
.paint(text, *codes) ⇒ Object
20 21 22 |
# File 'lib/everywhere/ui.rb', line 20 def paint(text, *codes) color? ? "\e[#{codes.join(";")}m#{text}\e[0m" : text.to_s end |
.phase(msg) ⇒ Object
72 |
# File 'lib/everywhere/ui.rb', line 72 def phase(msg) = puts(phase_line(msg)) |
.phase_line(msg) ⇒ Object
--- output levels --------------------------------------------------------
phase ● a major phase boundary (build / sign / notarize)
step → a top-level action inside a phase
substep ▸ a sub-action under a step (indented)
detail · raw-ish detail under a substep (indented + dim)
Every level has a *_line string builder (used by LogFilter, which returns lines for someone else to print) and a printer that puts it. ok / success / warn / bad / die! carry outcomes at any level.
63 |
# File 'lib/everywhere/ui.rb', line 63 def phase_line(msg) = "\n#{bold(cyan("●"))} #{bold(msg)}" |
.red(t) ⇒ Object
26 |
# File 'lib/everywhere/ui.rb', line 26 def red(t) = paint(t, 31) |
.short_path(str) ⇒ Object
Collapse the noisy machine paths that external tools echo (codesign,
notarytool, stapler) down to something a human can scan. In particular the
runner works out of a deep /var/folders/…/every-runner…/src/ sandbox — we
strip that prefix so only the meaningful dist/App.app tail remains. Then
$HOME → ~ and the cwd → a leading (dropped) prefix.
167 168 169 170 171 172 173 174 175 176 |
# File 'lib/everywhere/ui.rb', line 167 def short_path(str) s = str.to_s.dup # Runner/local temp sandboxes: keep only the part after the extracted src/. s = s.gsub(%r{/(?:private/)?(?:var|tmp)/[^\s"']*?/every-[a-z]+[0-9A-Za-z_-]+/src/}, "") # Any other everywhere-* scratch dir: keep the basename tail. s = s.gsub(%r{/(?:private/)?(?:var|tmp)/[^\s"']*?/everywhere-[a-z]+[0-9A-Za-z_.-]*/}, "") home = ENV["HOME"] s = s.gsub("#{home}/", "~/") if home && !home.empty? s end |
.step(msg) ⇒ Object
73 |
# File 'lib/everywhere/ui.rb', line 73 def step(msg) = puts(step_line(msg)) |
.step_line(msg) ⇒ Object
64 |
# File 'lib/everywhere/ui.rb', line 64 def step_line(msg) = "#{cyan("→")} #{msg}" |
.substep(msg) ⇒ Object
74 |
# File 'lib/everywhere/ui.rb', line 74 def substep(msg) = puts(substep_line(msg)) |
.substep_line(msg) ⇒ Object
65 |
# File 'lib/everywhere/ui.rb', line 65 def substep_line(msg) = " #{cyan("▸")} #{msg}" |
.success(msg) ⇒ Object
79 |
# File 'lib/everywhere/ui.rb', line 79 def success(msg) = puts(success_line(msg)) |
.success_line(msg) ⇒ Object
70 |
# File 'lib/everywhere/ui.rb', line 70 def success_line(msg) = "#{green("✓")} #{bold(msg)}" |
.target_label(target) ⇒ Object
"macos-arm64" → "macOS-arm64"
47 48 49 50 |
# File 'lib/everywhere/ui.rb', line 47 def target_label(target) os, arch = target.to_s.split("-", 2) [ os_label(os), arch ].compact.join("-") end |
.warn(msg) ⇒ Object
78 |
# File 'lib/everywhere/ui.rb', line 78 def warn(msg) = puts(warn_line(msg)) |
.warn_line(msg) ⇒ Object
69 |
# File 'lib/everywhere/ui.rb', line 69 def warn_line(msg) = "#{yellow("!")} #{msg}" |
.yellow(t) ⇒ Object
28 |
# File 'lib/everywhere/ui.rb', line 28 def yellow(t) = paint(t, 33) |
.yn(bool) ⇒ Object
A yes/no fact, colored by truthiness (for signing receipts).
94 |
# File 'lib/everywhere/ui.rb', line 94 def yn(bool) = bool ? green("yes") : red("no") |