Skip to content

fix: harden bottle registry and surface silent bottle-creation failures - #136

Open
frankea wants to merge 4 commits into
mainfrom
claude/review-open-issues-f11c5x
Open

fix: harden bottle registry and surface silent bottle-creation failures#136
frankea wants to merge 4 commits into
mainfrom
claude/review-open-issues-f11c5x

Conversation

@frankea

@frankea frankea commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Follow-up to the new reports on #61 and #134 (both from the same user on 2026-07-25). Reviewing the remaining failure paths turned up four gaps that can still make bottles disappear silently or leave the user with no feedback; this PR closes all four.

Changes

1. An unreadable bottle registry is no longer wiped (Refs #61)

BottleData.init used to respond to a failed decode of BottleVM.plist by immediately re-encoding an empty registry over it — every registered bottle vanished from the sidebar with no error and no way back. Now:

  • The paths-only fallback shape (written by encodeFallback(), which the primary decoder could never read back) is salvaged before the file is treated as corrupt.
  • A genuinely unreadable file is moved aside to BottleVM.corrupt-<timestamp>.plist instead of being overwritten, and an alert on launch tells the user where it went and that their bottle folders were not deleted.
  • A version-mismatch decode now keeps the registered paths and upgrades the file format instead of re-writing the stale version on every launch.

2. Registry save failures now surface (Refs #61)

BottleCreationError.persistenceSaveFailed existed but was never thrown — BottleData.encode()'s failure result was silently discarded, so a bottle that couldn't be saved looked created and then vanished on the next launch. Bottle creation now goes through registerBottlePath(_:), which verifies the entries file on disk actually contains the new path and throws into the existing failure alert (with copyable diagnostics) when it doesn't.

3. Missing Wine runtime fails fast with a way out (Refs #61)

Creating a bottle without WhiskyWine installed (failed/skipped first-run download) previously died mid-creation with a low-level file-not-found error. The preflight now checks isWhiskyWineInstalled() and throws a dedicated runtimeMissing error, and the failure alert gains a Run Setup… button that opens the setup sheet directly.

4. Winetricks no longer silently does nothing (Refs #134)

Winetricks.runCommand returned without any feedback when the bundled resources couldn't be located. It now shows a critical alert with reinstall guidance, matching the headless install path's behavior.

Also adds the six new localized strings (English source; Crowdin sync picks them up) and a [Unreleased] changelog entry.

Testing

  • Localizable.xcstrings validated as JSON; new keys inserted in Xcode's sorted format.
  • Line lengths and file headers checked against .swiftlint.yml.
  • Existing UI tests are unaffected (the create-bottle test cancels without creating, so the new runtime preflight never triggers).
  • Not compiled locally (no macOS toolchain in this environment) — relying on CI for build + tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_014NkZVxUeRT7ejcP7mppS2D


Generated by Claude Code

- An unreadable bottle registry is now moved aside (with an alert saying
  where) and paths are recovered from the fallback format when possible,
  instead of being overwritten with an empty list on startup
- Bottle creation verifies the registry write on disk and raises the
  previously never-thrown persistenceSaveFailed error, so a failed save
  shows the failure alert instead of the bottle vanishing on next launch
- Creating a bottle without the WhiskyWine runtime installed fails fast
  with an actionable "runtime missing" error and a Run Setup button
- The Winetricks Terminal flow shows an error when the bundled winetricks
  resources are missing instead of silently doing nothing

Refs #61, #134

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NkZVxUeRT7ejcP7mppS2D
claude added 2 commits July 26, 2026 00:36
SwiftLint flagged runCommandInternal (function_body_length 59/50) and the
ContentView struct (type_body_length 272/250) after the new error alerts.
Extract the repair-failed and missing-resources alerts into helpers (the
former deduplicates an identical inline alert in repairPrefixAndRetry) and
move ContentView's sidebar/detail/filteredBottles into a same-file
extension. No behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NkZVxUeRT7ejcP7mppS2D
SwiftFormat's blankLinesAtEndOfScope flagged the leftover blank line from
moving the subview builders into an extension. Verified clean with the
same SwiftFormat 0.58.7 Linux binary and config CI uses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NkZVxUeRT7ejcP7mppS2D
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 55.00000% with 63 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
Whisky/Utils/Winetricks.swift 0.00% 20 Missing ⚠️
...hiskyKit/Sources/WhiskyKit/Whisky/BottleData.swift 78.57% 18 Missing ⚠️
Whisky/View Models/BottleVM.swift 0.00% 16 Missing ⚠️
Whisky/Views/ContentView.swift 55.00% 9 Missing ⚠️

📢 Thoughts on this report? Let us know!

Codecov's patch gate requires 60% coverage of the WhiskyKit diff; the new
BottleData logic had none. Make the registry location injectable (internal
init(entriesFile:), following the WhiskyWineInstaller pattern of
parameterizing paths for testability) and add BottleDataTests covering
first-run creation, register-and-reload round trips, idempotent
registration, corrupt-registry backup, and salvage of the paths-only
fallback format. Production behavior is unchanged - init() still uses the
standard registry location.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NkZVxUeRT7ejcP7mppS2D
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants