Print banner before cobra parsing; route arg errors through ui.Error
Two output-style fixes plus a quiet-mode correction.
Banner: a manual scan of os.Args in CLIEntry decides whether to suppress
the banner (--quiet/-q/--cron), then prints it before cobra parses any
arguments. This makes the banner appear even when cobra rejects bad args
("requires at least 2 arg(s)") and on --help — paths that previously
skipped PersistentPreRun entirely. The cobra-side hook plumbing (sync.Once,
PersistentPreRun, custom HelpFunc) is removed.
Errors: rootCmd.SilenceErrors = true so cobra no longer prints its own
"Error: <msg>" line. Any error returned from Execute() goes through
ui.New(os.Stderr).Error(...), giving the documented "🛑 ERROR: <msg>"
format. A new helper cli.ReportError() formats errors from goroutine
paths that can't return through cobra's normal return chain; every
CLI command's fx-goroutine error path now calls it alongside the
existing structured log.Error so both channels record the failure.
Quiet mode: previously --quiet/--cron swapped Vaultik.UI to io.Discard,
which silenced Warning and Error messages too — contradicting the
documented "suppresses non-error output" semantics. ui.Writer now has
a SetQuiet flag that drops Begin/Complete/Info/Notice/Detail/Progress/
Banner only; Warning and Error always emit.
Also folds in restore.go cleanups the audit flagged: the hardcoded
"WARNING:" prefix on the failed-files block now uses ui.Warning +
ui.Detail, the post-restore "Restored N files" line uses ui.Complete,
and the "No files found to restore" branch emits both log.Warn and
ui.Warning so structured logs continue to capture it under --verbose.
This commit is contained in:
@@ -49,9 +49,15 @@ const Marker = "》"
|
||||
// It also counts warnings and errors emitted so the caller can summarize at
|
||||
// the end of an operation ("Finished successfully." vs "Finished with
|
||||
// warnings.").
|
||||
//
|
||||
// When Quiet is set, Begin/Complete/Info/Notice/Detail/Progress/Banner
|
||||
// are silently dropped, but Warning and Error always emit. This honors
|
||||
// the convention that --quiet "Suppresses non-error output" — warnings
|
||||
// and errors are by definition not suppressible.
|
||||
type Writer struct {
|
||||
out io.Writer
|
||||
color bool
|
||||
quiet bool
|
||||
warnings int
|
||||
errors int
|
||||
}
|
||||
@@ -70,6 +76,13 @@ func NewWithColor(out io.Writer, color bool) *Writer {
|
||||
return &Writer{out: out, color: color}
|
||||
}
|
||||
|
||||
// SetQuiet toggles the writer's quiet mode. In quiet mode all message
|
||||
// classes are silenced except Warning and Error.
|
||||
func (w *Writer) SetQuiet(quiet bool) { w.quiet = quiet }
|
||||
|
||||
// Quiet reports whether the writer is in quiet mode.
|
||||
func (w *Writer) Quiet() bool { return w.quiet }
|
||||
|
||||
// Out returns the underlying writer.
|
||||
func (w *Writer) Out() io.Writer { return w.out }
|
||||
|
||||
@@ -100,21 +113,33 @@ func (w *Writer) paint(color, s string) string {
|
||||
|
||||
// Begin prints an operation-start line, left-aligned with a white marker.
|
||||
func (w *Writer) Begin(format string, args ...any) {
|
||||
if w.quiet {
|
||||
return
|
||||
}
|
||||
w.emit(ansiWhite, Marker, "", format, args)
|
||||
}
|
||||
|
||||
// Complete prints an operation-completion line in green, left-aligned.
|
||||
func (w *Writer) Complete(format string, args ...any) {
|
||||
if w.quiet {
|
||||
return
|
||||
}
|
||||
w.emit(ansiGreen, Marker, ansiGreen, format, args)
|
||||
}
|
||||
|
||||
// Info prints a neutral status line, left-aligned with a white marker.
|
||||
func (w *Writer) Info(format string, args ...any) {
|
||||
if w.quiet {
|
||||
return
|
||||
}
|
||||
w.emit(ansiWhite, Marker, "", format, args)
|
||||
}
|
||||
|
||||
// Notice prints an attention-worthy informational line, marker in cyan.
|
||||
func (w *Writer) Notice(format string, args ...any) {
|
||||
if w.quiet {
|
||||
return
|
||||
}
|
||||
w.emit(ansiCyan, Marker, "", format, args)
|
||||
}
|
||||
|
||||
@@ -139,6 +164,9 @@ func (w *Writer) Error(format string, args ...any) {
|
||||
// Distinct from Progress (semantically a "heartbeat") in usage but
|
||||
// visually identical.
|
||||
func (w *Writer) Detail(format string, args ...any) {
|
||||
if w.quiet {
|
||||
return
|
||||
}
|
||||
w.emit(ansiWhite, " "+Marker, "", format, args)
|
||||
}
|
||||
|
||||
@@ -150,12 +178,18 @@ func (w *Writer) ErrorCount() int { return w.errors }
|
||||
|
||||
// Progress prints an indented heartbeat / per-item update, marker in white.
|
||||
func (w *Writer) Progress(format string, args ...any) {
|
||||
if w.quiet {
|
||||
return
|
||||
}
|
||||
w.emit(ansiWhite, " "+Marker, "", format, args)
|
||||
}
|
||||
|
||||
// Banner prints a line with no marker, left-aligned. Bold when color
|
||||
// is enabled. Used for the application startup banner only.
|
||||
func (w *Writer) Banner(format string, args ...any) {
|
||||
if w.quiet {
|
||||
return
|
||||
}
|
||||
body := fmt.Sprintf(format, args...)
|
||||
if w.color {
|
||||
body = ansiBold + body + ansiReset
|
||||
|
||||
Reference in New Issue
Block a user