feat(ui): complete internal/ui call-site migration#119
Merged
Conversation
Final PR of #105's three-PR phasing: every user-facing string in internal/cli now flows through ui.Writer.{Info, Success, Warn, Error, Println} — no direct cmd.Printf / fmt.Fprintf in business logic. Wraps up the rollout started by the scaffold (#74 PR1) and continued by the spinners (#117) and huh prompts (#118). What's migrated: - check.go: outputCheckJSON / outputCheckTable rewritten to take (ui.Writer, context.Context) instead of (cobra.Command). The table renderer still preserves the same field layout (LTS, Current, System node) so existing scripts piping `nodeup check | grep LTS` keep working. - list.go: outputListJSON / outputListTable rewritten the same way. Empty-state message now routes through writer.Info so FancyMode gets the bullet prefix while PlainMode stays clean. - packages.go: snapshot, list, restore, diff all migrated. The writeReport closure uses writerFromCmd for warn/info, the clearSentinel closure does too. The `Restored packages from %s` and `Restored packages for %s %s` lines route through writer.Success now (was cmd.Printf). - config.go: config show / get / set / init all migrated. The `set foo=bar (saved to ...)` and `wrote scaffolded config` lines are now Success messages; lock-release failures now go through writer.Warn. - upgrade.go: ~15 remaining cmd.Printf call sites migrated (sentinel cleanup, manager-resolution log, dry-run plan, upgrade-lock release, snapshot warnings, sentinel write warnings, restore warnings, migration report, active-version detection failure, cleanup error summary, completion line). All spinners / cleanup prompts / ResolveInteractive integration from #117/#118 are unchanged. - version.go: writerFromCmd made nil-context-safe (was: panicked on bare cobra.Command, which many tests construct). - list_test.go: newBufCmd now also redirects ErrOrStderr to the same buffer (mirrors how the migrated ui.Writer splits stdout vs stderr). outputListJSON / outputListTable tests rewritten to use writerFromCmd(cmd) for the writer argument. No new files, no go.mod changes. SPEC.md's V2 invariant now flips from aspirational to enforced: every user-facing string in internal/ and cmd/ routes through internal/ui. After this lands, issue #105 fully closes. Closes #105. Co-Authored-By: puku-ai-2.8 <noreply@puku.sh>
|
✔️ ac6a4ca - Conventional commits check succeeded. |
dipto0321
added a commit
that referenced
this pull request
Jul 5, 2026
16 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final PR of #105's three-PR phasing. Migrates every remaining user-facing
cmd.Printf/cmd.Println/fmt.Fprintfininternal/cli/*toui.Writer.{Info,Success,Warn,Error,Println}. SPEC.mdV2now flips from aspirational to enforced: nothing ininternal/orcmd/bypassesinternal/ui.What's in this PR
internal/cli/check.go—outputCheckJSON/outputCheckTablerewritten to take(ui.Writer, context.Context)instead of*cobra.Command. Same field layout (LTS, Current, System node) preserved so existingnodeup check | grep LTSpipelines keep working.internal/cli/list.go—outputListJSON/outputListTablerewritten the same way.internal/cli/packages.go—snapshot/list/restore/diffall migrated. ThewriteReportclosure andclearSentinelclosure usewriterFromCmdfor warn/info/success emission.internal/cli/config.go—show/get/set/initall migrated. Lock-release warnings now go throughwriter.Warn; success lines ("set foo=bar (saved to ...)", "wrote scaffolded config to ...") throughwriter.Success.internal/cli/upgrade.go— ~15 remainingcmd.Printfcall sites migrated: sentinel cleanup, manager-resolution log, dry-run plan, upgrade-lock release, snapshot warnings, sentinel-write warnings, restore warnings, migration-report paths, active-version-detection failure (#58), cleanup error summary, and the final "Upgrade complete!" line. The spinner wiring (#117) and cleanup prompt migration (#118) are unchanged.internal/cli/version.go—writerFromCmdmade nil-context-safe. Barecobra.Commandconstructions (common in unit tests) used to panic oncmd.Context(); now it returnscmd.OutOrStdout()/ErrOrStderrvia the existing fallback path.internal/cli/list_test.go—newBufCmdnow also redirectsErrOrStderrto the same buffer (mirroring how the migratedui.Writerroutes warn/error).outputListJSON/outputListTabletest signatures updated to passwriterFromCmd(cmd).Implementation notes
cmd.Printf("Warning: ...")→writerFromCmd(cmd).Warn(...)so warnings land instderrrather thanstdout(matters fornodeup upgrade | jqpipelines where Warn output would corrupt the JSON envelope).Successso FancyMode gets the ✓ glyph while PlainMode stays plain.Infoso FancyMode gets the • bullet — same rationale as Success.Printlnrather thanInfoso it doesn't get a bullet prefix (the line IS the header; bullets below it would be confusing).Warnbecause the user must see why their--cleanup/--yesshortcut was downgraded to per-version confirmation.Edge cases I considered before opening
writerFromCmdwith barecobra.Command(noSetContext): previously crashed with a nil-deref. Now falls through tocmd.OutOrStdout()/ErrOrStderr— same fallback path that production code already took whenwriterCtxKeywas missing. Pinned by every existing test that builds a barecobra.CommandvianewBufCmd.newBufCmdnow redirects both to the samebytes.Buffer. Without this fix, the migration'sWarncalls would silently write to the real os.Stderr during tests and the assertions would look incomplete (we hit exactly this bug — see the failingTestOutputListTable_MixedStatesrun during development).upgradeflow has the most complex output surface (spinners + warnings + success + summary), so testing the simpler commands' migrations first caught the nil-context / writer-wiring issues before touching upgrade. The upgrade-related tests are unchanged because they don't construct a barecobra.Command— they go throughNewRootCmdwhich sets the context properly.writerFromCmd(cmd)called from a defer (lock release warnings):cmdis captured by the closure, so the defer works correctly. No lifetime issues.cmd.Printftowriter.Success— that's a deliberate category change. Old behavior was an unstructured info line; new behavior (FancyMode ✓) emphasizes the success. PlainMode is byte-identical except for the absence of\rcleanup (we never had\rin the old cmd.Printf either; this is a no-op for piped output).Verification
Plus manual smoke tests:
What this enables
cmd.Printf/cmd.Println/fmt.Fprintlnininternal/cli/and gate new contributions.ui.DecideMode) now really IS a single decision point — before this PR, callers that bypassedui.Writer(e.g., the oldfmt.Fprintf(cmd.OutOrStdout(), ...)inconfig.go) made their own mode decision implicitly (always plain). NowDecideModeruns once inroot.go'sPersistentPreRunEand every output observes its result.ui.Writer(which used to be onlyupgrade.go's "Using manager", "Upgrade complete", and a few others).Closes #105 (last of 3 PRs). No follow-up PRs planned; the Spinner + Prompt + Writer abstractions are stable.