bakery: add search, completions, rollback, verify, purge, dry-run, self-update

New CLI surface, approved for review before merge:

- search <query>: case-insensitive name/description substring match
- completions <shell>: bash/zsh/fish/elvish/powershell via clap_complete
- rollback <pkg>: restore the previously installed version from a local
  pre-update binary backup (not a network re-fetch — index.json's minisign
  signature only covers the current published version, so pinning an old
  version from the server would only be checkable against its unsigned
  per-version .sha256 sidecar, a materially weaker trust path)
- verify [pkg]: recompute installed binaries' sha256 and compare against
  the hash recorded at install time, not a fresh index lookup (the index
  only has the latest release's checksum, which may not match what's
  actually installed)
- remove --purge: additionally remove the license dir, desktop entry, and
  data dir, each gated through the existing confirm() prompt; config is
  still deliberately left alone
- self-update: documented entry point for updating bakery itself
- --dry-run: global flag, short-circuits right before install::
  install_package in both the install and update paths
- download progress: chunked read loop in manifest::fetch_bytes prints
  periodic \r progress on stderr when Content-Length is present and stderr
  is a tty
- update --all output: "already at X" is now DIM with a neutral glyph
  instead of GREEN, plus a bold one-line summary count, so unchanged
  packages don't visually compete with ones that actually changed

InstalledPackage gained previous_version and binary_sha256 (both
#[serde(default)]) to back rollback/verify. fetch_and_place now returns the
verified sha256 instead of discarding it.

Also fixes a handful of pre-existing clippy lints in files this touches
(manual split_once, &PathBuf-vs-&Path, derivable Default, unnecessary
unwrap) surfaced by a clippy version newer than when that code was last
touched — confirmed via git stash that they predate this branch. bread-
utils has one more of these (suspicious_open_options in singleton.rs) left
alone: the mechanical fix would truncate the PID file before the
lock-held-by-another-process branch reads its contents, which would break
toggle_or_kill's PID lookup, so cargo clippy -p bakery needs --no-deps
until that one's fixed with actual thought.
This commit is contained in:
Breadway 2026-08-05 17:10:31 +08:00
parent d45fc422f2
commit eba8cb6c44
9 changed files with 785 additions and 69 deletions

View file

@ -44,6 +44,16 @@ pub fn fail(s: &str) -> String {
style(&format!("{s}"), RED)
}
/// Neutral "nothing to do" glyph, dim rather than green — for steady-state
/// noise like "already at latest" in `bakery update --all`, where most
/// packages hit this every run. Reusing GREEN there drowns out the
/// packages that actually changed, and meaning shouldn't depend on color
/// alone (an unusual terminal palette can make BOLD/GREEN/DIM look similar),
/// so this also carries its own glyph the way `ok`/`fail` do.
pub fn unchanged(s: &str) -> String {
style(&format!("· {s}"), DIM)
}
#[cfg(test)]
mod tests {
use super::*;
@ -57,4 +67,14 @@ mod tests {
fn dev_badge_is_nonempty() {
assert!(!track_badge(Track::Dev).is_empty());
}
#[test]
fn unchanged_carries_a_distinct_glyph_from_ok_and_fail() {
// Meaning must survive even with colors stripped (NO_COLOR, or a
// terminal palette that makes ANSI codes look alike) — so the glyph
// itself has to differ, not just the color.
assert!(unchanged("foo").contains('·'));
assert!(!ok("foo").contains('·'));
assert!(!fail("foo").contains('·'));
}
}