Commit Graph

2 Commits

Author SHA1 Message Date
be786fe25e Gate prune's local-cleanup prose on --json and give make build a rule (closes #108)
All checks were successful
check / check (pull_request) Successful in 2m29s
CleanupLocalSnapshots wrote three prose lines to stdout with no --json
awareness, and they covered every branch of the function, so no input
avoided them: `vaultik prune --json | jq` failed even after the banner
fix. -q never helped either, because printlnStdout and stdoutf write
straight to Vaultik.Stdout and never consult Vaultik.UI, which is what
SetQuiet affects.

The issue offered three fixes and asked for a decision. Taken: thread
*PruneOptions into the function and gate each write on !opts.JSON,
matching PruneBlobs -- its sibling phase, which already takes the same
struct -- along with RemoveSnapshot and remote info, so the package has
one pattern rather than two. Rejected: moving the lines to log.Info,
because the logger's default level is slog.LevelWarn, so that would not
relocate them to stderr, it would delete them from a plain `vaultik
prune`, and the removal of rows from the local index is not something to
narrate only under --verbose. Also rejected: putting the stale-record
count into PruneBlobsResult, whose every field is blob-scoped and which
is produced by the later phase; a prune document covering both phases is
a reasonable thing to want, but that is a schema design question, not a
stream-hygiene fix. The two events are duplicated as log.Info records,
which PruneBlobs already does alongside its own prints, so they survive
on stderr under --verbose.

Also closes #110. `make build` printed "Nothing to be done for 'build'"
and exited 0 without producing a binary: build was listed in .PHONY with
no build: rule anywhere, and declaring a name phony is exactly what
converts make's "No rule to make target" error into a silent success.
Fixed with `build: vaultik`, keeping vaultik: as the file rule. Audited
all 19 .PHONY names: build was the only one without a rule, and vaultik
is correctly absent from .PHONY, being a real file target.

Tests, each verified to fail with the fix reverted rather than assumed
to. CleanupLocalSnapshots leaves stdout untouched under --json in all
three branches (stale records, none, empty index) and still emits every
line without it, so the guard cannot be satisfied by deleting the
output. prune --json runs end to end through Entry, cobra and fx over
the process's real stdout descriptor against a file:// store, asserting
exactly one JSON document, in both the stale and non-stale branches. And
a parse of the Makefile asserts every .PHONY name has a rule and that
build reaches the rule producing the binary, which keeps the audit true
for names added later; it is a parse rather than an invocation of make
because `make test` is what runs it, so shelling back into `make build`
would nest a build inside the test run. The property a parse cannot
establish -- that the recipe still fails when the build fails -- was
verified by hand against a deliberately broken tree: make build exits 2
and produces nothing.

cmd/vaultik gains its first test file, so `make test` now reports 16
packages ok where it reported 15. flagConfig and programName constants
are extracted in the CLI tests because the new argument vector pushed
"--config" and "vaultik" over goconst's threshold.

README's stdout/stderr section described the banner as "the other thing
that writes to stdout", which this defect contradicted; it now states
the contract that holds, which is that stdout under --json is the
document and nothing else, for prune as well as for the other four.
2026-08-09 17:33:06 +00:00
f21e7c9e70 Suppress the startup banner for --json (closes #106)
All checks were successful
check / check (push) Successful in 2m31s
The banner is printed to stdout before cobra parses, and
bannerSuppressedInArgs recognised only --quiet, -q and --cron. So every
--json document was preceded by two banner lines and a blank one, and
`vaultik snapshot list --json | jq` failed. Passing opts.JSON as
extraQuiet did not help: that calls UI.SetQuiet in an fx OnStart hook,
long after Entry has printed.

The raw-argv scan is extended rather than the banner moved after
parsing. root.go documents that the banner must survive cobra rejecting
its arguments and --help, and no single post-parse location covers those
paths. The subcommand-versus-persistent distinction does not decide it:
--cron is already in the suppression list and is itself subcommand-only,
existing on snapshot create alone, so this adds another instance of an
accepted imprecision rather than a new kind. The error directions are
asymmetric - a false positive loses a decorative banner, a false negative
corrupts a document - so the scan errs toward suppression, which is also
why --json=false suppresses, exactly as --quiet=false already does.

Four of the five --json commands now pipe into jq cleanly with no other
flags: snapshot list, snapshot verify, snapshot remove, remote info.
prune does not, because pruneLocalSnapshots writes three prose lines to
stdout with no --json awareness. That reproduces identically before this
change and -q never suppressed it either, since printlnStdout and
stdoutf bypass v.UI entirely. Tracked as #108.

Also fixed: TTYHandler's human-readable byte formatting did not survive
grouping, because the key check compared against the bare attribute name
and a grouped record presents it qualified. AGENTS.md policy 9 keyed the
log format on stdout's TTY-ness, which #82 made false by moving the
logger to stderr; it now names the log stream. Vaultik.Stderr keeps its
field with the comment amended to say outright that nothing writes to
it, and the dead listEnv.stderr is removed.
2026-08-09 19:18:36 +02:00