bread-ecosystem/bakery/src/ui.rs
Breadway 4ac54c610d
All checks were successful
dev bread-theme / build (push) Successful in 13s
dev bakery / build (push) Successful in 1m2s
bakery: add stable/beta/dev build tracks
Adds a track concept to bakery (separate from the existing bakery/pacman
distribution channel): stable (unchanged tag-triggered releases), beta
(deliberate beta-v* tag promotion), and dev (published on every push to
dev). Each track gets its own signed index + artifact tree under
dl.breadway.dev so stable's paths and existing installs are untouched.

- bakery: new Track type, a global track preference in installed.json
  (defaults to stable via serde, no migration needed), `bakery track
  show`/`set`, a BAKERY_INDEX_BASE_URL override for testing, and a real
  semver comparison in `update` (was a plain string-equality check before).
  ANSI-colored/aligned CLI output (TTY + NO_COLOR aware).
- gen-index.sh: TRACK env var selects which subtree to read/write.
- CI: dev-bakery.yml/beta-bakery.yml/dev-bread-theme.yml/beta-bread-theme.yml
  publish those two products on the new tracks; dev/beta skip the GitHub
  Release upload step (no per-commit release spam).
- docs/release-channels.md documents the three-track policy.
2026-07-22 09:19:31 +08:00

60 lines
1.6 KiB
Rust

use crate::track::Track;
use std::io::IsTerminal;
pub const RESET: &str = "\x1b[0m";
pub const BOLD: &str = "\x1b[1m";
pub const DIM: &str = "\x1b[2m";
pub const RED: &str = "\x1b[31m";
pub const GREEN: &str = "\x1b[32m";
pub const YELLOW: &str = "\x1b[33m";
pub const CYAN: &str = "\x1b[36m";
pub const MAGENTA: &str = "\x1b[35m";
/// Colors are on only when stdout is a real terminal and `NO_COLOR` isn't
/// set — the ecosystem's existing CLI (breadcrumbs) hardcodes ANSI
/// unconditionally, which leaks escape codes into piped/logged output; this
/// is the hardening fix for that gap.
pub fn colors_enabled() -> bool {
std::env::var_os("NO_COLOR").is_none() && std::io::stdout().is_terminal()
}
pub fn style(s: &str, code: &str) -> String {
if colors_enabled() {
format!("{code}{s}{RESET}")
} else {
s.to_string()
}
}
/// `" [beta]"` / `" [dev]"`, colored — empty string for `Stable` so the
/// common-case output is unchanged.
pub fn track_badge(track: Track) -> String {
match track {
Track::Stable => String::new(),
Track::Beta => format!(" {}", style("[beta]", YELLOW)),
Track::Dev => format!(" {}", style("[dev]", MAGENTA)),
}
}
pub fn ok(s: &str) -> String {
style(&format!("{s}"), GREEN)
}
pub fn fail(s: &str) -> String {
style(&format!("{s}"), RED)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stable_badge_is_empty() {
assert_eq!(track_badge(Track::Stable), "");
}
#[test]
fn dev_badge_is_nonempty() {
assert!(!track_badge(Track::Dev).is_empty());
}
}