refactor: improve maintainability across pkg/ and cmd/#54
Conversation
Seven-phase plan covering: Reporter interface extraction, BootcInstaller removal, type consolidation, workflow pattern for install/update parity, raw output elimination, CLI flag structs, and cleanup pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7-phase plan with bite-sized tasks, TDD steps, exact file paths, code snippets, and verification checklist. Covers Reporter interface, BootcInstaller deletion, type consolidation, workflow pattern, raw output elimination, CLI flag structs, and cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all *ProgressReporter parameters with the Reporter interface
across 30 files. This includes:
- Function signatures: 35+ functions changed from progress *ProgressReporter
to progress Reporter
- Struct fields: Installer.progress, SystemUpdater.Progress,
ContainerExtractor.Progress, BootloaderInstaller.Progress,
BootcInstaller.Progress all changed to Reporter
- Constructors: NewTextReporter/NewJSONReporter/NoopReporter used instead
of NewProgressReporter
- Step() calls: Updated from 2-arg Step(step, name) to 3-arg
Step(step, total, name) to match new interface
- Test files: All NewProgressReporter(false, 1) replaced with NoopReporter{}
- cmd/ callers: Conditional Reporter creation pattern used
The old ProgressReporter type in pkg/progress.go is intentionally kept
for removal in the next task.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete InstallCallbacks, SetCallbacks, callOn* methods, callbackProgressAdapter, CreateCLICallbacks, and asLegacyProgress from pkg/install.go. Replace all callback invocations with direct Reporter method calls (Step, Message, MessagePlain, Warning, Error). Delete pkg/progress.go entirely -- the old ProgressReporter struct, type aliases, and re-exported constants are all superseded by pkg/reporter.go. Remove callback setup from cmd/install.go and cmd/interactive_install.go since NewInstaller now creates the appropriate Reporter internally based on cfg.JSONOutput. Delete tests for removed functionality: TestInstaller_SetCallbacks, TestInstaller_CallOnError, TestCreateCLICallbacks, and TestCallbackProgressAdapter. Update integration tests to remove SetCallbacks calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move CheckRequiredTools to pkg/tools.go, PullImage to pkg/container.go, and SetRootPasswordInTarget to pkg/system.go in preparation for removing the deprecated BootcInstaller type. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ypes Remove duplicate RebootPendingInfo from pkg/config.go, use types.RebootPendingInfo everywhere. Single source of truth. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ctx context.Context as the first parameter to all exported functions in pkg/ that do I/O or run external commands but previously lacked it. Convert exec.Command to exec.CommandContext where applicable to enable cancellation for long-running external commands. Updated functions: Download, Remove, Clear (ImageCache), InstallTmpfilesConfig, WriteSystemConfig, WriteSystemConfigToVar, UpdateSystemConfigImageRef, GetCurrentBootDeviceInfo, InstallDracutEtcOverlay, VerifyDracutEtcOverlay, SetupEtcOverlay, SetupEtcPersistence, InstallEtcMountUnit, PopulateEtcLower, SavePristineEtc, MergeEtcFromActive, EnsureCriticalFilesInOverlay, SetupSystemDirectories, PrepareMachineID, SetupLoopbackInstall, Cleanup (LoopbackDevice), SetRootPasswordInTarget. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove pkg/bootc.go and pkg/bootc_test.go entirely. The newer Installer API (NewInstaller) fully replaces it. Standalone functions were preserved in tools.go, container.go, system.go in prior commit. Update Makefile test-install target and copilot-instructions.md. Migrate readConfigFromFile helper to update_test.go. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Workflow runs a sequence of named StepFuncs with context cancellation and Reporter-based progress reporting. WorkflowState carries shared mutable state between steps. Both Install and Update flows will compose from shared steps using this abstraction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erifyContainer Deduplicate common install/update operations by extracting two shared helper functions in pkg/steps.go: - SetupTargetSystem: dracut module install, system directories, machine-id, etc.lower overlay, tmpfiles.d config - ExtractAndVerifyContainer: container extraction with verification Both install.go and update.go now call these shared functions instead of inline duplicated logic, removing ~115 lines of duplication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate all business logic output in pkg/ to use the Reporter interface instead of raw fmt.Print/Fprintf calls: - partition.go: warnings now use progress.Warning - dracut.go: output uses progress.Warning/MessagePlain - bootloader.go: add Reporter param to EFI/BOOT rename helpers - luks.go: add Reporter param to EnrollTPM2 - device_detect.go: add Reporter param to GetCurrentBootDevice - update.go: add Reporter param to GetInactiveRootPartition - lint.go: add Reporter field to Linter, remove stdout redirect hack - cache.go: add Reporter field to ImageCache, remove nil fallbacks Remaining fmt.Print calls are intentional: interactive prompts (LUKS passphrase, confirmation dialog), terminal spinner UI, Reporter internals, and test infrastructure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace module-level flag variables with typed flag structs for each command (installFlags, updateFlags, downloadFlags, lintFlags, etc.), improving code organization and making flag ownership explicit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update key components to document Reporter interface, shared steps, and Workflow type. Update console output conventions, install/update parity section, and data flow to reflect context propagation and typed flag structs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This is a comprehensive 7-phase refactoring that improves code maintainability, consistency, and modern Go patterns across the nbc codebase. The refactoring touches 48 files with a net reduction of ~130 lines while adding significant new abstractions. The PR replaces the concrete *ProgressReporter with a Reporter interface, removes 862 LOC of deprecated BootcInstaller code, consolidates duplicate types, adds context propagation throughout, and introduces shared step functions that enforce install/update parity by design.
Changes:
- Introduced
Reporterinterface withTextReporter,JSONReporter, andNoopReporterimplementations to decouple output handling - Removed deprecated
BootcInstallerand relocated utility functions to focused files (tools.go,system.go) - Added
context.Contextto all exported I/O functions and created sharedSetupTargetSystemandExtractAndVerifyContainerfunctions inpkg/steps.goto DRY up install/update flows - Encapsulated CLI flag variables into typed structs per command (eliminating module-level vars)
Reviewed changes
Copilot reviewed 48 out of 48 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/reporter.go | New Reporter interface with TextReporter, JSONReporter, NoopReporter implementations |
| pkg/reporter_test.go | Comprehensive tests for all Reporter implementations |
| pkg/workflow.go | New Workflow type for composable step-based orchestration |
| pkg/workflow_test.go | Tests for Workflow including error handling and cancellation |
| pkg/steps.go | Shared SetupTargetSystem and ExtractAndVerifyContainer functions enforce install/update parity |
| pkg/system.go, pkg/system_test.go | Relocated SetRootPasswordInTarget from deleted bootc.go |
| pkg/tools.go | Relocated CheckRequiredTools from deleted bootc.go |
| pkg/bootc.go, pkg/bootc_test.go | Deleted 862 LOC deprecated BootcInstaller code |
| pkg/progress.go | Deleted - replaced by reporter.go |
| pkg/install.go, pkg/update.go | Updated to use Reporter interface and shared steps, removed callback adapter code |
| pkg/partition.go, pkg/luks.go, pkg/disk.go, etc. | Changed from *ProgressReporter to Reporter parameter |
| pkg/etc_persistence.go, pkg/dracut.go, pkg/container.go, etc. | Added context.Context parameters and Reporter interface |
| pkg/config.go | Removed duplicate RebootPendingInfo, added context parameters |
| cmd/install.go, cmd/update.go, cmd/cache.go, etc. | Encapsulated flags into typed structs, removed module-level vars |
| cmd/interactive_install.go | Removed InstallCallbacks usage |
| docs/plans/2026-02-25-refactoring-design.md | Comprehensive refactoring design documentation |
| CLAUDE.md, .github/copilot-instructions.md | Updated to reflect new patterns and shared steps |
| Makefile | Updated test regex from TestBootcInstaller to TestInstaller |
| func readConfigFromFile(path string) (*SystemConfig, error) { | ||
| data, err := os.ReadFile(path) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| var config SystemConfig | ||
| if err := json.Unmarshal(data, &config); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return &config, nil | ||
| } |
There was a problem hiding this comment.
The readConfigFromFile helper function in update_test.go is only used by tests in that file. Consider moving it to a shared test helper file or internal test utilities package to make it more discoverable and reusable across test files. This would follow the pattern of other test utilities in pkg/testutil/.
|
|
||
| // GetCurrentBootDevice determines the disk device that the system booted from | ||
| func GetCurrentBootDevice() (string, error) { | ||
| func GetCurrentBootDevice(progress Reporter) (string, error) { |
There was a problem hiding this comment.
GetCurrentBootDevice is missing a context.Context parameter while GetCurrentBootDeviceInfo has one. For consistency and cancellation support, GetCurrentBootDevice should also accept ctx context.Context as its first parameter. This is especially important since it performs I/O operations that could benefit from cancellation.
|
|
||
| _test-install: ## Internal target for install tests | ||
| @go test -v ./pkg/... -run "^(TestBootcInstaller)" -timeout 20m | ||
| @go test -v ./pkg/... -run "^(TestInstaller)" -timeout 20m |
There was a problem hiding this comment.
The test regex pattern ^(TestInstaller) will match any test function starting with "TestInstaller", including "TestInstaller_Install", "TestInstaller_WithEncryption", etc. However, this pattern is less explicit than the original ^(TestBootcInstaller). Consider using ^TestInstaller (without parentheses) or ^TestIntegration_Installer to be more precise about which tests to run.
| @go test -v ./pkg/... -run "^(TestInstaller)" -timeout 20m | |
| @go test -v ./pkg/... -run "^TestIntegration_Installer" -timeout 20m |
Summary
Comprehensive refactoring to improve code maintainability, consistency, and modern Go patterns across the codebase. This is a 7-phase refactoring that touches 48 files with a net reduction of ~130 lines despite adding new abstractions.
Changes by phase:
*ProgressReporterwith aReporterinterface (TextReporter,JSONReporter,NoopReporter), enabling cleaner testing and output mode switchingBootcInstallertype and its tests, relocating standalone utility functions to focused files (pkg/system.go,pkg/tools.go)RebootPendingInfoto a single definition inpkg/types/context.Contextto all exported I/O functions; extractSetupTargetSystemandExtractAndVerifyContainerintopkg/steps.goto DRY up install/update flows; addWorkflowtype for composable step orchestrationfmt.Printcalls inpkg/withReporterinterface methods, threading the reporter through functions that previously wrote directly to stdout/stderrinstallFlags,updateFlags, etc.)Key architectural improvements:
pkg/steps.goenforces install/update parity — shared operations are defined oncepkg/output goes throughReporter, making JSON mode consistent and tests quietcontext.Contextflows through all I/O paths for proper cancellationTest plan
make buildpassesmake test-unitpasses (all tests except pre-existingTestCLI_VersionOutputgolden fixture mismatch from dirty git state)make lintpasses with 0 issuesmake test-integration(requires root)nbc installandnbc updateflows🤖 Generated with Claude Code