Skip to content

bundle cleanup: report failed removals - #24289

Merged
MikeMcQuaid merged 8 commits into
Homebrew:mainfrom
edel67:bundle-cleanup-failure-reporting
Oct 22, 2026
Merged

MikeMcQuaid merged 8 commits into
Homebrew:mainfrom
edel67:bundle-cleanup-failure-reporting

Conversation

@edel67

@edel67 edel67 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

brew bundle cleanup can print misleading removal counts and exit successfully after uninstall or untap commands fail. It now reports these failures through ofail, attempts the remaining removals and cache cleanup and exits with a non-zero status even if later commands succeed. Unexpected errors can still interrupt cleanup, and completed removals and trust changes are not rolled back.

A shared helper reports per-item uninstall failures across extension managers and uses successful commands for the Uninstalled count. Cask, formula and untap operations run in batches, so their failure messages warn that some items may already have been removed. Go cleanup, which deletes executables directly, now reports filesystem errors and counts only files it removes.

Reproduction

Run this isolated npm cleanup check from a Homebrew checkout. It uses /usr/bin/false to produce an uninstall failure without changing packages or trust, testing the handler's error output and failure flag rather than the full command's exit status.

HOMEBREW_DEVELOPER=1 HOMEBREW_NO_INSTALL_FROM_API=1 ./bin/brew ruby -- - <<'RUBY'
require "bundle"
require "bundle/extensions/npm"
Homebrew::Bundle::Npm.define_singleton_method(:package_manager_executable) do
  Pathname("/usr/bin/false")
end
Homebrew::Bundle::Npm.cleanup!(["example"])
puts "Failure recorded: #{Homebrew.failed?}"
RUBY
Result Original cleanup method Updated cleanup method
Error None Error: Failed to uninstall npm packages: example
Summary Uninstalled 1 npm packages Uninstalled 0 npm packages
Homebrew.failed? false true

Verification

./bin/brew lgtm --online passed on macOS, covering typechecking, style and all 10 affected spec files. The same 10 spec files passed on Ubuntu 24.04 ARM64: 287 examples, 0 failures. GitHub CI for 45d6b3895f passed 49 checks, with 4 checks skipped.


  • Have you followed our Contributing guidelines?
  • Have you checked for other open Pull Requests for the same change?
  • Have you explained what your changes do? Performance claims (e.g. "this is faster") must include brew benchmark results.
  • Have you explained why you'd like these changes included, not just what they do?
  • For bug fixes, have you given step-by-step brew commands to reproduce the bug?
  • Have you written new tests (excluding integration tests)? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) locally?

  • I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.

I used OpenAI Codex to help apply the revised design consistently across the Go, WinGet and Flatpak cleanup handlers, which I was less familiar with. It also helped draft the initial regression tests, narrow the patch and review the code. I reviewed the changes one code block at a time to understand and assess them myself.


Copilot AI lite review requested due to automatic review settings October 17, 2026 16:13

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diff is too big for this change. Please review it yourself and slim it down considerably.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c4f72038-2cb5-4585-b858-9f50ce030a3d
📥 Commits

Reviewing files that changed from the base of the PR and between e3adf67 and 769de0a.

📒 Files selected for processing: 8
  • Library/Homebrew/bundle/extensions/extension.rb
  • Library/Homebrew/bundle/extensions/flatpak.rb
  • Library/Homebrew/bundle/extensions/krew.rb
  • Library/Homebrew/bundle/extensions/mac_app_store.rb
  • Library/Homebrew/bundle/extensions/npm.rb
  • Library/Homebrew/bundle/extensions/vscode_extension.rb
  • Library/Homebrew/bundle/extensions/winget.rb
  • Library/Homebrew/test/bundle/uv_spec.rb
💤 Files with no reviewable changes: 4
  • Library/Homebrew/bundle/extensions/flatpak.rb
  • Library/Homebrew/bundle/extensions/vscode_extension.rb
  • Library/Homebrew/bundle/extensions/mac_app_store.rb
  • Library/Homebrew/bundle/extensions/winget.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Summary

Summary by CodeRabbit

  • Bug Fixes

    • Cleanup now clearly reports failed package, application, extension, formula, cask and tap removals.
    • Cleanup continues processing remaining items after individual failures while preserving failure status.
    • Missing executables and already-removed items are handled more reliably.
    • Success messages accurately count completed removals and use consistent package naming.
    • Cleanup commands now use the resolved package-manager executable consistently.
  • Tests

    • Expanded coverage for successful, failed, partial and no-op cleanup scenarios across supported package managers and extensions.

Brew Bundle cleanup now checks uninstall results, reports failed removals, and counts successful removals. The changes cover shared extension cleanup, package-manager-specific cleanup methods, and cask, formula, and tap cleanup commands.

Changes: Brew Bundle cleanup

Layer / File(s) Summary
Shared extension cleanup and result contract
Library/Homebrew/bundle/extensions/extension.rb, Library/Homebrew/bundle/extensions/{cargo,krew,npm,uv}.rb, Library/Homebrew/test/bundle/{cargo,krew,npm,uv}_spec.rb
Shared cleanup reports missing executables when removals are queued, reports individual uninstall failures, and counts successful uninstallations. Uninstall methods declare Boolean return values. Tests cover failed results, successful counts, continuation, and preservation of existing failure state.
Package-manager cleanup adapters
Library/Homebrew/bundle/extensions/{flatpak,go,mac_app_store,vscode_extension,winget}.rb, Library/Homebrew/test/bundle/{flatpak,go,mac_app_store,vscode_extension,winget}_spec.rb
Flatpak, Mac App Store, VS Code, and WinGet use per-item uninstall methods. Go cleanup reports missing executables and unlink failures. Tests cover command results, environment preservation, failure reporting, continued processing, and removal counts.
Cleanup subcommand failure handling
Library/Homebrew/bundle/subcommand/cleanup.rb, Library/Homebrew/test/cmd/bundle/cleanup_subcommand_spec.rb
The cleanup subcommand reports failed cask, formula, and tap commands. Tests require successful system-call results and cover failure state, suppressed success output, and continued cleanup processing.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 769de

No concrete merge-blocking risk remains from the reviewed changes.

Security architecture risk: 🔵 Low · up to e48c5

The change improves failure visibility while preserving the inspected cleanup targets and command privileges. Risk is limited to integrating the shared result contract across package managers. Cleanup remains non-transactional, and interruption and concurrent execution were not runtime-validated.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected changes affect packages and executables already reachable through the invoking cleanup process and external package managers. Flatpak retains system-wide removal, and WinGet retains exact ID and source selection. The provider refactoring does not establish a new privilege grant or broader removal scope.

Trust Boundaries and Controls

  • observed — The shared helper passes each selected item to its provider without constructing a shell command. Inspected provider sinks retain separate command arguments, and Go deletes the literal selected filesystem path. Flatpak now uses the resolved package-manager executable instead of a bare command name.

Resilience and Maintainability Implications

  • observed — Failure reporting sets Homebrew.failed without clearing earlier failures, preserving a nonzero terminal status after later successful work. The existing environment wrapper restores overridden variables in ensure, including exception unwinding. Inspected regression tests cover unsuccessful command results and failure retention after cache cleanup; they were not executed in this review.
Pre-merge checks: Passed 4
Check name Status Explanation
Description check Passed The description clearly explains the failed-removal reporting, failure propagation, successful-removal counting, tests, and verification covered by the changes.
Title check Passed The title clearly and concisely summarises the main change: reporting failed removals during bundle cleanup.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Resolve the duplicate qualified-formula receipt collision that can leave an installation unprotected.

1 open issue
What changed in this PR

Improves brew bundle cleanup by reporting failed removals, preserving failure status, and strengthening formula receipt protection.

Changes:

  • Adds failure reporting and accurate removal counts.
  • Improves manager-specific cleanup and receipt matching.
  • Adds regression tests and documentation.
File Summary
Library/​Homebrew/​test/​cmd/​bundle/​cleanup_subcommand_spec.rb Tests cleanup failure behavior.
Library/​Homebrew/​test/​bundle/​winget_spec.rb Tests WinGet cleanup handling.
Library/​Homebrew/​test/​bundle/​vscode_extension_spec.rb Tests VS Code extension cleanup.
Library/​Homebrew/​test/​bundle/​uv_spec.rb Tests uv cleanup handling.
Library/​Homebrew/​test/​bundle/​npm_spec.rb Tests npm failure reporting.
Library/​Homebrew/​test/​bundle/​mac_app_store_spec.rb Tests Mac App Store cleanup.
Library/​Homebrew/​test/​bundle/​krew_spec.rb Tests krew cleanup handling.
Library/​Homebrew/​test/​bundle/​go_spec.rb Tests Go executable cleanup.
Library/​Homebrew/​test/​bundle/​flatpak_spec.rb Tests Flatpak cleanup handling.
Library/​Homebrew/​test/​bundle/​cargo_spec.rb Tests Cargo cleanup handling.
Library/​Homebrew/​test/​bundle/​bundle_spec.rb Tests bundle receipt behavior.
Library/​Homebrew/​bundle/​subcommand/​cleanup.rb Reports failures and continues cleanup.
Library/​Homebrew/​bundle/​extensions/​winget.rb Updates WinGet cleanup behavior.
Library/​Homebrew/​bundle/​extensions/​vscode_extension.rb Updates VS Code cleanup behavior.
Library/​Homebrew/​bundle/​extensions/​uv.rb Updates uv cleanup behavior.
Library/​Homebrew/​bundle/​extensions/​npm.rb Reports npm uninstall failures.
Library/​Homebrew/​bundle/​extensions/​mac_app_store.rb Updates Mac App Store cleanup.
Library/​Homebrew/​bundle/​extensions/​krew.rb Updates krew cleanup behavior.
Library/​Homebrew/​bundle/​extensions/​go.rb Reports executable deletion errors.
Library/​Homebrew/​bundle/​extensions/​flatpak.rb Updates Flatpak cleanup behavior.
Library/​Homebrew/​bundle/​extensions/​extension.rb Adds shared cleanup failure handling.
Library/​Homebrew/​bundle/​extensions/​cargo.rb Updates Cargo cleanup behavior.
Library/​Homebrew/​bundle.rb Improves formula receipt protection.
docs/​Brew-Bundle-and-Brewfile.md Documents cleanup failure semantics.

Review effort: Lite

Comment thread Library/Homebrew/bundle.rb Outdated
@edel67
edel67 force-pushed the bundle-cleanup-failure-reporting branch from 594c06b to b58a83c Compare October 17, 2026 18:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments: 1
Library/Homebrew/test/cmd/bundle/cleanup_subcommand_spec.rb: 1

668-673: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the CLI exit status for cleanup failures.

The failure examples call described_class.cleanup directly. They check Homebrew.failed? and stderr, but they do not exercise the brew bundle cleanup process. The normal brew path exits with status 1 when Homebrew.failed? is true. Add a command-level failure example that asserts the non-zero exit status, stderr, and relevant stdout. The related tests also call cleanup directly, so they do not detect a regression in the CLI exit status.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f1b2d764-52a0-4ce6-adc7-7a883fda8533
📥 Commits

Reviewing files that changed from the base of the PR and between 594c06b and b58a83c.

📒 Files selected for processing: 17
  • Library/Homebrew/bundle/extensions/extension.rb
  • Library/Homebrew/bundle/extensions/flatpak.rb
  • Library/Homebrew/bundle/extensions/go.rb
  • Library/Homebrew/bundle/extensions/mac_app_store.rb
  • Library/Homebrew/bundle/extensions/vscode_extension.rb
  • Library/Homebrew/bundle/extensions/winget.rb
  • Library/Homebrew/bundle/subcommand/cleanup.rb
  • Library/Homebrew/test/bundle/cargo_spec.rb
  • Library/Homebrew/test/bundle/flatpak_spec.rb
  • Library/Homebrew/test/bundle/go_spec.rb
  • Library/Homebrew/test/bundle/krew_spec.rb
  • Library/Homebrew/test/bundle/mac_app_store_spec.rb
  • Library/Homebrew/test/bundle/npm_spec.rb
  • Library/Homebrew/test/bundle/uv_spec.rb
  • Library/Homebrew/test/bundle/vscode_extension_spec.rb
  • Library/Homebrew/test/bundle/winget_spec.rb
  • Library/Homebrew/test/cmd/bundle/cleanup_subcommand_spec.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread Library/Homebrew/test/cmd/bundle/cleanup_subcommand_spec.rb
@edel67

edel67 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor Author

Actionable comments posted: 1

🧹 Nitpick comments: 1

Library/Homebrew/test/cmd/bundle/cleanup_subcommand_spec.rb: 1> 668-673: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the CLI exit status for cleanup failures.
The failure examples call described_class.cleanup directly. They check Homebrew.failed? and stderr, but they do not exercise the brew bundle cleanup process. The normal brew path exits with status 1 when Homebrew.failed? is true. Add a command-level failure example that asserts the non-zero exit status, stderr, and relevant stdout. The related tests also call cleanup directly, so they do not detect a regression in the CLI exit status.

ℹ️ Review info
⚙️ Run configuration
📥 Commits
📒 Files selected for processing: 17
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

A CLI test would add coverage, but the code that converts Homebrew.failed? to exit status 1 is unchanged. These tests check that failures are recorded and preserved through later cleanup. I’m leaving the additional CLI test out to keep this fix focused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments: 1
Library/Homebrew/test/cmd/bundle/cleanup_subcommand_spec.rb: 1

668-673: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

The direct assertions cover Homebrew.failed? and the failure message, but they do not exercise the CLI entrypoint that converts that state into process exit status 1. A command-level example should cover the non-zero exit status and the observable output from the remaining successful cleanup.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9c41281f-4226-4e5f-ade7-0302b788397f
📥 Commits

Reviewing files that changed from the base of the PR and between b58a83c and d334ab1.

📒 Files selected for processing: 21
  • Library/Homebrew/bundle/extensions/cargo.rb
  • Library/Homebrew/bundle/extensions/extension.rb
  • Library/Homebrew/bundle/extensions/flatpak.rb
  • Library/Homebrew/bundle/extensions/go.rb
  • Library/Homebrew/bundle/extensions/krew.rb
  • Library/Homebrew/bundle/extensions/mac_app_store.rb
  • Library/Homebrew/bundle/extensions/npm.rb
  • Library/Homebrew/bundle/extensions/uv.rb
  • Library/Homebrew/bundle/extensions/vscode_extension.rb
  • Library/Homebrew/bundle/extensions/winget.rb
  • Library/Homebrew/bundle/subcommand/cleanup.rb
  • Library/Homebrew/test/bundle/cargo_spec.rb
  • Library/Homebrew/test/bundle/flatpak_spec.rb
  • Library/Homebrew/test/bundle/go_spec.rb
  • Library/Homebrew/test/bundle/krew_spec.rb
  • Library/Homebrew/test/bundle/mac_app_store_spec.rb
  • Library/Homebrew/test/bundle/npm_spec.rb
  • Library/Homebrew/test/bundle/uv_spec.rb
  • Library/Homebrew/test/bundle/vscode_extension_spec.rb
  • Library/Homebrew/test/bundle/winget_spec.rb
  • Library/Homebrew/test/cmd/bundle/cleanup_subcommand_spec.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@edel67

edel67 commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

This diff is too big for this change. Please review it yourself and slim it down considerably.

I removed receipt fixes, parsing refactors and unrelated cleanup. I kept the code that detects failed removals, reports them, counts successes correctly and preserves the failure status, plus tests and documentation for that behavior. It still touches 22 files because the package managers have separate handlers, but the changed-line total dropped by ~80%.

@edel67
edel67 requested a review from MikeMcQuaid October 18, 2026 06:21

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking better, thanks! Perhaps needs a rebase and ideally a lot more DRYing up.

Comment thread Library/Homebrew/bundle/extensions/flatpak.rb Outdated
Comment thread Library/Homebrew/bundle/extensions/extension.rb Outdated
@edel67
edel67 force-pushed the bundle-cleanup-failure-reporting branch from d334ab1 to e48c597 Compare October 20, 2026 01:52
@edel67

edel67 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@MikeMcQuaid The shared cleanup now uses package_description with Utils.pluralize, but that changes "Krew plugin" to "krew plugin" and "Mac App Store app" to "app".

I'd prefer to preserve the existing wording without duplicating labels or changing naming elsewhere. Is there an existing way you'd suggest reusing those names? Otherwise, would a cleanup_description method defaulting to package_description, with overrides for the exceptions, be reasonable? I've kept that change out pending your feedback.

Comment thread docs/Brew-Bundle-and-Brewfile.md Outdated
Comment thread Library/Homebrew/bundle/extensions/extension.rb Outdated
@MikeMcQuaid

Copy link
Copy Markdown
Member

The shared cleanup now uses package_description with Utils.pluralize, but that changes "Krew plugin" to "krew plugin" and "Mac App Store app" to "app".

I'd prefer to preserve the existing wording without duplicating labels or changing naming elsewhere.

I think it's preferable to avoid the duplication rather than get keep that casing as-is. If you feel strongly: please add another generic method for this rather than having the repetition.

Otherwise, would a cleanup_description method defaulting to package_description, with overrides for the exceptions, be reasonable?

Yup sounds good.

@edel67

edel67 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

The shared cleanup now uses package_description with Utils.pluralize, but that changes "Krew plugin" to "krew plugin" and "Mac App Store app" to "app".
I'd prefer to preserve the existing wording without duplicating labels or changing naming elsewhere.

I think it's preferable to avoid the duplication rather than get keep that casing as-is. If you feel strongly: please add another generic method for this rather than having the repetition.

Otherwise, would a cleanup_description method defaulting to package_description, with overrides for the exceptions, be reasonable?

Yup sounds good.

Honestly, app is less informative than Mac App Store app, but the existing labels remain accurate, and preserving the wording doesn't justify another method and five overrides for this PR. I’ll keep the naming code as currently pushed.

@edel67
edel67 requested a review from MikeMcQuaid October 20, 2026 23:06

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Report failed Go metadata lookups. · go.rb:101-115

Library/Homebrew/bundle/extensions/go.rb:101-115
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Report failed Go metadata lookups.

When a configured Go cleanup item runs, Utils.popen_read returns output for a failed go env or go version -m command because both calls use the unsafe default. cleanup! does not inspect the command status. A failed metadata lookup can therefore skip the executable and report Removed 0 Go executables without setting Homebrew.failed. Handle these subprocess failures at this cleanup boundary.

Suggested fix
-          gobin = Utils.popen_read(go, "env", "GOBIN", err: :err).chomp
-          gopath = Utils.popen_read(go, "env", "GOPATH", err: :err).chomp
+          begin
+            gobin = Utils.popen_read(go, "env", "GOBIN", err: :err, safe: true).chomp
+            gopath = Utils.popen_read(go, "env", "GOPATH", err: :err, safe: true).chomp
+          rescue ErrorDuringExecution => e
+            ofail "Failed to inspect Go environment: #{e}"
+            return
+          end
...
-            output = Utils.popen_read(go, "version", "-m", binary, err: File::NULL)
+            begin
+              output = Utils.popen_read(go, "version", "-m", binary, err: File::NULL, safe: true)
+            rescue ErrorDuringExecution => e
+              ofail "Failed to inspect Go executable #{binary}: #{e}"
+              next
+            end

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b3bd36c9-e64a-4d26-b458-b5997713a9af
📥 Commits

Reviewing files that changed from the base of the PR and between 45d6b38 and e3adf67.

📒 Files selected for processing: 2
  • Library/Homebrew/bundle/extensions/go.rb
  • Library/Homebrew/bundle/subcommand/cleanup.rb
🚧 Files skipped from review as they are similar to previous changes: 1
  • Library/Homebrew/bundle/subcommand/cleanup.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more question and good to go.

Comment thread Library/Homebrew/bundle/extensions/extension.rb

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great now, thanks for the work here and for persisting through the feedback. You rock!

@MikeMcQuaid
MikeMcQuaid added this pull request to the merge queue Oct 22, 2026
Merged via the queue into Homebrew:main with commit a94e18a Oct 22, 2026
52 of 53 checks passed
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.

3 participants