Skip to content

Commit 83f0b5c

Browse files
authored
fix: wrap all bare error returns with context (#107)
* refactor: replace interface{} with typed CaptureResults in snapshot capture * fix: wrap all bare error returns with context Wrap ~80 bare `return err` and `return nil, err` statements across 24 production files with `fmt.Errorf("context: %w", err)` to satisfy the project convention of never returning unwrapped errors. Four intentional exceptions remain: - dotfiles.go:301 — filepath.WalkDir callback convention - install.go:150 — installer.Run already wraps thoroughly - installer.go:226 — Apply already wraps thoroughly - auth/login.go:154 — pollOnce returns user-facing messages Also reverted wrapping in listBackupsSorted since callers check os.IsNotExist (which does not traverse %w chains).
1 parent 2932d6a commit 83f0b5c

27 files changed

Lines changed: 195 additions & 173 deletions

internal/auth/login.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type cliPollResponse struct {
5555
func LoginInteractive(ctx context.Context, apiBase string) (*StoredAuth, error) {
5656
codeID, code, err := startAuthSession(apiBase)
5757
if err != nil {
58-
return nil, err
58+
return nil, fmt.Errorf("start auth session: %w", err)
5959
}
6060

6161
fmt.Fprintf(os.Stderr, "\n")
@@ -72,7 +72,7 @@ func LoginInteractive(ctx context.Context, apiBase string) (*StoredAuth, error)
7272

7373
result, err := pollForApproval(ctx, apiBase, codeID)
7474
if err != nil {
75-
return nil, err
75+
return nil, fmt.Errorf("poll for approval: %w", err)
7676
}
7777

7878
expiresAt, err := time.Parse(time.RFC3339, result.ExpiresAt)

internal/brew/brew.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func ListOutdated() ([]OutdatedPackage, error) {
8787
}
8888

8989
if err := json.Unmarshal(output, &result); err != nil {
90-
return nil, err
90+
return nil, fmt.Errorf("parse brew outdated: %w", err)
9191
}
9292

9393
var outdated []OutdatedPackage
@@ -246,10 +246,10 @@ func CheckDiskSpace() (float64, error) {
246246
var stat syscall.Statfs_t
247247
home, err := system.HomeDir()
248248
if err != nil {
249-
return 0, err
249+
return 0, fmt.Errorf("check disk space: %w", err)
250250
}
251251
if err := syscall.Statfs(home, &stat); err != nil {
252-
return 0, err
252+
return 0, fmt.Errorf("statfs: %w", err)
253253
}
254254
// stat.Bsize is uint32 on darwin and int64 on linux; the kernel always
255255
// reports a non-negative block size so the conversion is safe.

internal/cli/install.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func runInstallCmd(cmd *cobra.Command, args []string) error {
104104
if installCfg.RemoteConfig == nil {
105105
src, err := resolveInstallSource(cmd, args)
106106
if err != nil {
107-
return err
107+
return fmt.Errorf("resolve install source: %w", err)
108108
}
109109

110110
if src.kind == sourceSyncSource {
@@ -113,7 +113,7 @@ func runInstallCmd(cmd *cobra.Command, args []string) error {
113113
}
114114

115115
if err := applyInstallSource(src); err != nil {
116-
return err
116+
return fmt.Errorf("apply install source: %w", err)
117117
}
118118
}
119119

@@ -128,7 +128,7 @@ func runInstallCmd(cmd *cobra.Command, args []string) error {
128128
} else if !installCfg.Silent && (!installCfg.DryRun || system.HasTTY()) {
129129
rc, proceed, err := promptCustomizeAndApply(installCfg.RemoteConfig)
130130
if err != nil {
131-
return err
131+
return fmt.Errorf("customize config: %w", err)
132132
}
133133
if !proceed {
134134
ui.Info("Cancelled.")

internal/cli/snapshot.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func runSnapshot(cmd *cobra.Command) error {
7979
if localFlag || publishFlag {
8080
snap, err := captureEnvironment()
8181
if err != nil {
82-
return err
82+
return fmt.Errorf("capture environment: %w", err)
8383
}
8484
if dryRunFlag {
8585
showSnapshotPreview(snap)
@@ -97,7 +97,7 @@ func runSnapshot(cmd *cobra.Command) error {
9797
}
9898
if publishFlag {
9999
if err := publishSnapshot(cmd.Context(), snap, slugFlag); err != nil {
100-
return err
100+
return fmt.Errorf("publish snapshot: %w", err)
101101
}
102102
}
103103
return nil
@@ -110,7 +110,7 @@ func runSnapshot(cmd *cobra.Command) error {
110110

111111
snap, err := captureEnvironment()
112112
if err != nil {
113-
return err
113+
return fmt.Errorf("capture environment: %w", err)
114114
}
115115

116116
if dryRunFlag {
@@ -123,7 +123,7 @@ func runSnapshot(cmd *cobra.Command) error {
123123

124124
edited, confirmed, err := reviewSnapshot(snap)
125125
if err != nil {
126-
return err
126+
return fmt.Errorf("review snapshot: %w", err)
127127
}
128128
if !confirmed {
129129
return nil
@@ -153,7 +153,7 @@ func interactiveSaveOrPublish(ctx context.Context, snap *snapshot.Snapshot) erro
153153
}
154154
choice, err := ui.SelectOption("What to do with this snapshot?", options)
155155
if err != nil {
156-
return err
156+
return fmt.Errorf("select snapshot action: %w", err)
157157
}
158158

159159
switch choice {
@@ -260,7 +260,7 @@ func captureJSONSnapshot() error {
260260
func captureEnvironment() (*snapshot.Snapshot, error) {
261261
snap, err := captureWithUI()
262262
if err != nil {
263-
return nil, err
263+
return nil, fmt.Errorf("capture with ui: %w", err)
264264
}
265265
catalogMatch := snapshot.MatchPackages(snap)
266266
snap.CatalogMatch = *catalogMatch
@@ -298,7 +298,7 @@ func captureWithUI() (*snapshot.Snapshot, error) {
298298
func reviewSnapshot(snap *snapshot.Snapshot) (*snapshot.Snapshot, bool, error) {
299299
edited, confirmed, err := ui.RunSnapshotEditor(snap)
300300
if err != nil {
301-
return nil, false, err
301+
return nil, false, fmt.Errorf("snapshot editor: %w", err)
302302
}
303303
if !confirmed {
304304
fmt.Fprintln(os.Stderr)

internal/cli/snapshot_import.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
func runSnapshotImport(importPath string, dryRun bool) error {
1818
snap, err := loadSnapshot(importPath)
1919
if err != nil {
20-
return err
20+
return fmt.Errorf("load snapshot: %w", err)
2121
}
2222

2323
if snap.Health.Partial {
@@ -29,7 +29,7 @@ func runSnapshotImport(importPath string, dryRun bool) error {
2929
fmt.Fprintln(os.Stderr)
3030
proceed, err := ui.Confirm("Continue with partial snapshot?", false)
3131
if err != nil {
32-
return err
32+
return fmt.Errorf("confirm partial snapshot: %w", err)
3333
}
3434
if !proceed {
3535
fmt.Fprintln(os.Stderr)
@@ -43,7 +43,7 @@ func runSnapshotImport(importPath string, dryRun bool) error {
4343

4444
edited, confirmed, err := ui.RunSnapshotEditor(snap)
4545
if err != nil {
46-
return err
46+
return fmt.Errorf("snapshot editor: %w", err)
4747
}
4848
if !confirmed {
4949
fmt.Fprintln(os.Stderr)
@@ -54,7 +54,7 @@ func runSnapshotImport(importPath string, dryRun bool) error {
5454

5555
ok, err := confirmInstallation(edited, dryRun)
5656
if err != nil {
57-
return err
57+
return fmt.Errorf("confirm installation: %w", err)
5858
}
5959
if !ok {
6060
fmt.Fprintln(os.Stderr)
@@ -96,17 +96,17 @@ func loadSnapshot(importPath string) (*snapshot.Snapshot, error) {
9696
fmt.Fprintf(os.Stderr, " Downloading snapshot from %s...\n", importPath)
9797
data, err := downloadSnapshotBytes(importPath, &http.Client{Timeout: apiRequestTimeout})
9898
if err != nil {
99-
return nil, err
99+
return nil, fmt.Errorf("download snapshot: %w", err)
100100
}
101101
s, err := snapshot.ParseBytes(data)
102102
if err != nil {
103-
return nil, err
103+
return nil, fmt.Errorf("parse snapshot: %w", err)
104104
}
105105
snap = s
106106
default:
107107
s, err := snapshot.LoadFile(importPath)
108108
if err != nil {
109-
return nil, err
109+
return nil, fmt.Errorf("load snapshot file: %w", err)
110110
}
111111
snap = s
112112
}

internal/cli/snapshot_publish.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ func publishSnapshot(ctx context.Context, snap *snapshot.Snapshot, explicitSlug
6565
fmt.Fprintln(os.Stderr, " Publishing as a new config on openboot.dev")
6666
configName, configDesc, visibility, err = promptPushDetails("")
6767
if err != nil {
68-
return err
68+
return fmt.Errorf("prompt publish details: %w", err)
6969
}
7070
}
7171

7272
resultSlug, err := postSnapshotToAPI(snap, configName, configDesc, visibility, stored.Token, apiBase, targetSlug)
7373
if err != nil {
74-
return err
74+
return fmt.Errorf("publish snapshot: %w", err)
7575
}
7676

7777
recordPublishResult(stored.Username, resultSlug, targetSlug, visibility, apiBase)

internal/cli/update.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func runRollback() error {
133133

134134
func runPinnedUpgrade(v string) error {
135135
if err := updater.ValidateSemver(v); err != nil {
136-
return err
136+
return fmt.Errorf("validate version: %w", err)
137137
}
138138
if updateIsHomebrewInstall() {
139139
ui.Warn("OpenBoot is managed by Homebrew — --version is not supported.")

internal/config/packages_remote.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func loadRemotePackages(dryRun bool) ([]remotePackage, error) {
6767
// Fetch from server.
6868
pkgs, err := fetchRemotePackages()
6969
if err != nil {
70-
return nil, err
70+
return nil, fmt.Errorf("load remote packages: %w", err)
7171
}
7272

7373
// Write cache (best-effort) — skip during dry-run to avoid disk side effects.
@@ -121,12 +121,12 @@ var cacheDir = func() string {
121121
func readPackagesCache() ([]remotePackage, error) {
122122
data, err := os.ReadFile(filepath.Join(cacheDir(), packagesCacheFile))
123123
if err != nil {
124-
return nil, err
124+
return nil, fmt.Errorf("read packages cache: %w", err)
125125
}
126126

127127
var entry packagesCacheEntry
128128
if err := json.Unmarshal(data, &entry); err != nil {
129-
return nil, err
129+
return nil, fmt.Errorf("parse packages cache: %w", err)
130130
}
131131

132132
if time.Since(entry.FetchedAt) > packagesCacheTTL {
@@ -139,7 +139,7 @@ func readPackagesCache() ([]remotePackage, error) {
139139
func writePackagesCache(pkgs []remotePackage) error {
140140
dir := cacheDir()
141141
if err := os.MkdirAll(dir, 0700); err != nil {
142-
return err
142+
return fmt.Errorf("mkdir packages cache: %w", err)
143143
}
144144

145145
entry := packagesCacheEntry{
@@ -148,7 +148,7 @@ func writePackagesCache(pkgs []remotePackage) error {
148148
}
149149
data, err := json.Marshal(entry)
150150
if err != nil {
151-
return err
151+
return fmt.Errorf("marshal packages cache: %w", err)
152152
}
153153

154154
return os.WriteFile(filepath.Join(dir, packagesCacheFile), data, 0600)

internal/config/remote.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func fetchConfigByAlias(apiBase, alias, token string) (*RemoteConfig, error) {
264264
func decodeRemoteConfig(r io.Reader) (*RemoteConfig, error) {
265265
data, err := io.ReadAll(io.LimitReader(r, 1<<20))
266266
if err != nil {
267-
return nil, err
267+
return nil, fmt.Errorf("read remote config: %w", err)
268268
}
269269
return UnmarshalRemoteConfigFlexible(data)
270270
}
@@ -284,7 +284,7 @@ func UnmarshalRemoteConfigFlexible(data []byte) (*RemoteConfig, error) {
284284
// Extract packages as typed objects and convert to flat arrays.
285285
var raw map[string]json.RawMessage
286286
if err := json.Unmarshal(data, &raw); err != nil {
287-
return nil, err
287+
return nil, fmt.Errorf("parse remote config: %w", err)
288288
}
289289

290290
pkgData, ok := raw["packages"]
@@ -341,7 +341,7 @@ func UnmarshalRemoteConfigFlexible(data []byte) (*RemoteConfig, error) {
341341

342342
var result RemoteConfig
343343
if err := json.Unmarshal(normalised, &result); err != nil {
344-
return nil, err
344+
return nil, fmt.Errorf("unmarshal normalised config: %w", err)
345345
}
346346
normalizeRemoteConfig(&result)
347347
backfillMacOSPrefsFromSnapshot(&result, data)

internal/config/validate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ func ValidateDotfilesURL(rawURL string) error {
6565

6666
func (rc *RemoteConfig) Validate() error {
6767
if err := validatePackageLists(rc); err != nil {
68-
return err
68+
return fmt.Errorf("validate packages: %w", err)
6969
}
7070
if err := ValidateDotfilesURL(rc.DotfilesRepo); err != nil {
7171
return fmt.Errorf("invalid dotfiles_repo: %w", err)
7272
}
7373
if err := validateMacOSPrefs(rc); err != nil {
74-
return err
74+
return fmt.Errorf("validate macos prefs: %w", err)
7575
}
7676
return validatePostInstall(rc)
7777
}

0 commit comments

Comments
 (0)