Skip to content
Logo

Contributing

Setup

  1. Fork and clone the repository
  2. make setup to build all dependencies
  3. make test to verify everything works
  4. Install swift-format: brew install swift-format
  5. Install Node.js + pnpm — make monaco builds the editor bundle (Boo/Resources/MonacoBundle, gitignored) and runs before make build, make test, and make release

Code Style

Boo uses swift-format (config in .swift-format).

make format   # Format all source files (monaco-format + swift-format)
make lint     # Check for violations (monaco-lint + swift-format; CI runs this)

Rules: 4-space indent, 120-char lines, zero compiler warnings, all UI on main thread.

Ghostty

make ghostty-patches   # Apply Vendor/patches/*.patch to Vendor/ghostty (idempotent)
make clean-ghostty     # Force a GhosttyKit rebuild
make ghostty-wasm      # Rebuild ghostty-vt.wasm for the Remote Control web UI

After updating the submodule, re-sync CGhostty/include/ghostty.h from Vendor/ghostty/include/ghostty.h (make ghostty does this) and keep a zig matching Ghostty's minimum_zig_version.

Making Changes

  1. Create a feature branch from main (e.g., feature/plugin-name, fix/tab-close-crash)
  2. Make changes
  3. make format && make test && make lint
  4. Push and open a pull request

Adding a Plugin

  1. Create Boo/Plugins/YourPlugin/YourPlugin.swift conforming to BooPluginProtocol
  2. Register in PluginRegistry.registerBuiltins()
  3. Add tests in Tests/BooTests/Plugins/YourPlugin/

See Plugin Development for the full guide.

Testing

make test                              # Run all tests
swift test --filter PluginTests        # Run specific suite

Local Development Builds

  • make run strips SUFeedURL and SUPublicEDKey from the temporary debug app bundle in .build/debug/Boo.app
  • That disables Sparkle for local development so debug builds never poll the production appcast
  • make app, make zip, make dmg, and make dist keep the release Sparkle configuration intact
  • Manual release testing should therefore use make app, make zip, or make dmg, not make run

Release

Releases are automated via GitHub Actions using semantic-release with conventional commits.

make dist   # Manual: release build + sign + notarize + ZIP

Requires: SIGNING_IDENTITY, APPLE_ID, APPLE_TEAM_ID, APPLE_PASSWORD env vars. For Sparkle appcast signing in CI, also set SPARKLE_PRIVATE_KEY.

Release flow summary:

  1. semantic-release computes the next version and updates Boo/App/Info.plist
  2. scripts/prepare-release.sh sets both CFBundleShortVersionString and CFBundleVersion, builds the release app bundle, signs it, optionally notarizes it, and only then packages the already-signed app into a ZIP archive
  3. scripts/update-appcast.sh generates an appcast artifact from the freshly built ZIP archive
  4. semantic-release commits the version bump
  5. The release workflow injects the generated appcast into the documentation site artifact and deploys GitHub Pages

Conventions

  • TerminalColor.cgColor/.nsColor for color conversions
  • AppSettings.shared with auto-notification for settings
  • RemoteExplorer.shellEscPath() for remote paths
  • AppSettings.shared.pluginBool/pluginString/pluginDouble for plugin settings