Skip to content

Rewrite babble as modular Ruby application#1

Draft
Copilot wants to merge 7 commits intomainfrom
copilot/rewrite-babble-as-ruby-app
Draft

Rewrite babble as modular Ruby application#1
Copilot wants to merge 7 commits intomainfrom
copilot/rewrite-babble-as-ruby-app

Conversation

Copy link
Copy Markdown

Copilot AI commented Feb 12, 2026

Replaces ksh implementation with clean Ruby architecture that orchestrates Homebrew, Mac App Store, and macOS system upgrades. Eliminates config duplication and fixes validation bugs from prototype.

Architecture

Entry flow: Bash wrapper (bin/babble) loads brew.env files, sources Homebrew's utils/ruby.sh for portable Ruby, execs orchestrator.

Module structure:

lib/babble/
├── orchestrator.rb      # Sequential flow: brew → mas → macos
├── config_manager.rb    # Shared validation (eliminates BrewUpgrade/MasUpgrade duplication)
├── brew_upgrade.rb      # Detect outdated → quit apps → upgrade → reopen
├── mas_upgrade.rb       # Same workflow for Mac App Store
├── macos_update.rb      # softwareupdate wrapper
├── app_manager.rb       # Lifecycle: detect (lsappinfo) → quit (JXA) → reopen (BundleLauncher)
├── bundle_launcher.rb   # Multi-tier reopen: open -b → launchctl asuser → lsregister
├── quarantine_purger.rb # Remove com.apple.quarantine xattrs from cask bundles
└── waiter.rb           # Interactive prompts with custom continuation messages

Swift GUI: Auto-compiled on first use, cached to ~/.cache/babble/quit_alert. Supports dark/light mode via embedded base64 SVG icons.

Config Example

apps:
  homebrew:
    - token: iterm2
      bundle_ids: [com.googlecode.iterm2]
      unsafe_to_quit: true  # Shows native alert before quit
  mas:
    - app_id: 1595464182
      name: MonitorControlLite
      bundle_ids: [app.monitorcontrol.MonitorControlLite]
      unsafe_to_quit: true

Fixes

  • quit_message validation: Fixed variable scope bug where existing_value/new_value referenced wrong block
  • ReDoS vulnerability: Replaced /[a-z0-9_.-]+(?:\.[a-z0-9_.-]+)+/i (nested quantifiers) with non-backtracking pattern
  • Message flow: Added continuation_message parameter to eliminate duplicate "Continuing..." output
  • yq integration: Soft dependency for config reorganization (skips gracefully if missing)

Standards

All Ruby modules use typed: strict and frozen_string_literal: true. Zero commented-out code. Shared constants extracted (CACHE_DIR).

Original prompt

Overview

Rewrite babble as a clean, modular Ruby application that orchestrates upgrades for Homebrew (formulae + casks), Mac App Store apps, and macOS system updates. This replaces the current ksh implementation on main with a new Ruby-based architecture.

The existing prototype on the refactor/modular branch should be used as reference for understanding intent, but the implementation should be clean — no commented-out code, no debug prints, no duplicated modules.

Key Design Decisions

Architecture

  • Bash entry point (bin/babble): Loads brew.env files, sources Homebrew's utils/ruby.sh to find HOMEBREW_RUBY_PATH, then execs the Ruby orchestrator. This pattern is essential and must be preserved faithfully from the prototype's refactor/bin/babble_claude_main.
  • Ruby orchestrator: Modular library under lib/babble/ with clear separation of concerns.
  • Swift GUI: Native macOS NSAlert for "unsafe to quit" apps. Auto-compiled on first use via xcrun swiftc and cached locally. No pre-built binaries shipped — macOS will refuse to run unsigned/unnotarized distributed binaries.

Module Structure

File Responsibility
lib/babble/config_manager.rb Load, validate, and merge the unified YAML config. Shared by brew and MAS — eliminates the massive duplication between the prototype's BrewUpgrade and MasUpgrade modules.
lib/babble/brew_upgrade.rb Outdated detection (brew outdated --json=v2), quit-before-upgrade, brew upgrade, reopen-after-upgrade. Delegates config to ConfigManager, app lifecycle to AppManager.
lib/babble/mas_upgrade.rb mas outdated, quit-before-upgrade, mas upgrade, reopen. Same delegation pattern.
lib/babble/macos_update.rb softwareupdate wrapper.
lib/babble/app_manager.rb Detect running apps (via lsappinfo), quit (via JXA/osascript), invoke Swift GUI for unsafe_to_quit apps, reopen (via BundleLauncher).
lib/babble/bundle_launcher.rb Multi-tier app reopen logic: NSWorkspace.openApplicationlaunchctl asuser fallback → lsregister re-registration. Based on prototype's MacUtils::BundleLauncher.
lib/babble/quarantine_purger.rb Remove Gatekeeper quarantine/provenance xattrs from cask app bundles. Based on prototype's QuarantinePurger.
lib/babble/waiter.rb Interactive terminal prompts with space-bar-to-continue.
lib/babble/orchestrator.rb Top-level flow: brew_upgrade → mas_upgrade → macos_update in sequence.

Entry Points

File Role
bin/babble Bash wrapper: initialize Homebrew env, load brew.env, source utils/ruby.sh, exec Ruby orchestrator
lib/babble/cli.rb Ruby CLI entry point invoked by the bash wrapper

Configuration

The unified YAML config file (config/apps.yml) defines apps with their bundle IDs, unsafe_to_quit flags, and quit_messages. Structure from prototype:

apps:
  homebrew:
    - token: iterm2
      bundle_ids:
        - com.googlecode.iterm2
      unsafe_to_quit: true
    - token: adobe-acrobat-reader
      bundle_ids:
        - com.adobe.AdobeRdrCEFHelper
        - com.adobe.Reader
        - com.adobe.Reader.helper
      unsafe_to_quit: false
  mas:
    - app_id: 1595464182
      name: MonitorControlLite
      bundle_ids:
        - app.monitorcontrol.MonitorControlLite
      unsafe_to_quit: true

Swift Quit Alert GUI

  • Source: swift/src/quit_alert.swift — NSAlert with dark/light mode SVG icons (base64 encoded), localized Cancel/Continue buttons
  • Auto-compiled via xcrun swiftc on first use, cached to ~/.cache/babble/quit_alert (or $XDG_CACHE_HOME/babble/)
  • Compiled for both x86_64 and arm64 targeting macOS 13+
  • The alert message pattern: "{AppName} cannot be open during installation. Click Continue to quit {AppName} and begin the update. The application will open when the update is complete."
  • Exit codes: 0 = Continue (user approved quit), 1 = Cancel, 2 = icon load failure, 3 = usage error

Waiter Improvements

The Waiter.waiter method must accept a custom continuation message parameter:

def self.waiter(key, continuation_message: "Continuing...")

This eliminates duplicate/misleading messages. Callers pass context-appropriate strings:

  • Waiter.waiter("run_command", continuation_message: "Upgrading outdated packages...")
  • Waiter.waiter("run_command", continuation_message: "Checking for outdated packages...")
  • Waiter.waiter("next_section", continuation_message: "Proceeding to Mac App Store upgrades...")

Message Flow Fix

The prototype shows duplicate messages. The correct flow:

Stage Message
Before waiter Preparing to upgrade all outdated packages...
Waiter prompt → Run command: Press Space bar to continue, or Ctrl-C to exit.
After space bar Custom continuation message from caller (NOT generic "Continuing...")
Actual upgrade (brew upgrade output...

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 6 commits February 12, 2026 02:50
Co-authored-by: toobuntu <674390+toobuntu@users.noreply.github.com>
Co-authored-by: toobuntu <674390+toobuntu@users.noreply.github.com>
Co-authored-by: toobuntu <674390+toobuntu@users.noreply.github.com>
Co-authored-by: toobuntu <674390+toobuntu@users.noreply.github.com>
Co-authored-by: toobuntu <674390+toobuntu@users.noreply.github.com>
Co-authored-by: toobuntu <674390+toobuntu@users.noreply.github.com>
Copilot AI changed the title [WIP] Rewrite babble as a clean Ruby application Rewrite babble as modular Ruby application Feb 12, 2026
Copilot AI requested a review from toobuntu February 12, 2026 02:57
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