ota deployment script, lots of other fun goodies too
This commit is contained in:
129
bringup/fmt.py
Normal file
129
bringup/fmt.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""Terminal formatting helpers for the bring-up tool.
|
||||
|
||||
Thin wrapper around ANSI escapes so stages.py / bringup.py can emit
|
||||
consistently-styled headings, prompts, status tags, and result
|
||||
summaries without sprinkling raw escape codes everywhere.
|
||||
|
||||
Colors auto-disable when stdout is not a TTY or when the `NO_COLOR`
|
||||
environment variable is set (see no-color.org).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def _color_supported() -> bool:
|
||||
if os.environ.get("NO_COLOR") is not None:
|
||||
return False
|
||||
if not sys.stdout.isatty():
|
||||
return False
|
||||
# Modern Windows 10+ terminals support VT100 once any ANSI sequence has
|
||||
# been emitted — a no-op system("") call flips the flag on cmd.exe.
|
||||
if os.name == "nt":
|
||||
try:
|
||||
os.system("")
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
|
||||
|
||||
_USE = _color_supported()
|
||||
|
||||
|
||||
def _c(code: str) -> str:
|
||||
return f"\x1b[{code}m" if _USE else ""
|
||||
|
||||
|
||||
RESET = _c("0")
|
||||
BOLD = _c("1")
|
||||
DIM = _c("2")
|
||||
|
||||
RED = _c("31")
|
||||
GREEN = _c("32")
|
||||
YELLOW = _c("33")
|
||||
BLUE = _c("34")
|
||||
MAGENTA = _c("35")
|
||||
CYAN = _c("36")
|
||||
|
||||
|
||||
def stage(title: str) -> str:
|
||||
"""Big block heading that opens a stage."""
|
||||
bar = "-" * 60
|
||||
return (
|
||||
f"\n{CYAN}{bar}{RESET}\n"
|
||||
f"{BOLD}{CYAN} {title}{RESET}\n"
|
||||
f"{CYAN}{bar}{RESET}"
|
||||
)
|
||||
|
||||
|
||||
def section(title: str) -> str:
|
||||
"""Smaller sub-heading inside a stage."""
|
||||
return f"\n{BOLD}{MAGENTA}-- {title} --{RESET}"
|
||||
|
||||
|
||||
def prompt(text: str) -> str:
|
||||
return f"{YELLOW}{text}{RESET}"
|
||||
|
||||
|
||||
def tag(label: str, color: str) -> str:
|
||||
return f"[{color}{BOLD}{label}{RESET}]"
|
||||
|
||||
|
||||
OK_TAG = tag("OK", GREEN)
|
||||
ERR_TAG = tag("ERR", RED)
|
||||
SKIP_TAG = tag("SKIP", YELLOW)
|
||||
WARN_TAG = tag("WARN", YELLOW)
|
||||
INFO_TAG = tag("INFO", BLUE)
|
||||
EVT_TAG = tag("EVT", CYAN)
|
||||
|
||||
|
||||
def status_tag(status: str) -> str:
|
||||
s = (status or "").upper()
|
||||
return {
|
||||
"OK": OK_TAG,
|
||||
"ERR": ERR_TAG,
|
||||
"SKIP": SKIP_TAG,
|
||||
"WARN": WARN_TAG,
|
||||
}.get(s, tag(s or "?", DIM))
|
||||
|
||||
|
||||
def fail(text: str) -> str:
|
||||
return f"{RED}{BOLD}{text}{RESET}"
|
||||
|
||||
|
||||
def pass_(text: str) -> str:
|
||||
return f"{GREEN}{BOLD}{text}{RESET}"
|
||||
|
||||
|
||||
def warn(text: str) -> str:
|
||||
return f"{YELLOW}{text}{RESET}"
|
||||
|
||||
|
||||
def dim(text: str) -> str:
|
||||
return f"{DIM}{text}{RESET}"
|
||||
|
||||
|
||||
_ANSI_RE = None
|
||||
|
||||
|
||||
def strip(text: str) -> str:
|
||||
"""Return `text` with all ANSI escape sequences removed.
|
||||
|
||||
Used by the transcript writer so log files don't contain `\x1b[...m`
|
||||
garbage when stdout is colored.
|
||||
"""
|
||||
global _ANSI_RE
|
||||
if _ANSI_RE is None:
|
||||
import re
|
||||
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
|
||||
return _ANSI_RE.sub("", text)
|
||||
|
||||
|
||||
def summary_line(passed: int, failed: int, warnings: int, skipped: int) -> str:
|
||||
color = GREEN if failed == 0 else RED
|
||||
return (f" {color}{BOLD}pass={passed}{RESET} "
|
||||
f"{RED if failed else DIM}{BOLD}fail={failed}{RESET} "
|
||||
f"{YELLOW if warnings else DIM}warn={warnings}{RESET} "
|
||||
f"{DIM}skip={skipped}{RESET}")
|
||||
Reference in New Issue
Block a user