Contributing
Setup
- Fork and clone the repository
make setupto build all dependenciesmake testto verify everything works- Install swift-format:
brew install swift-format - Install Node.js + pnpm —
make monacobuilds the editor bundle (Boo/Resources/MonacoBundle, gitignored) and runs beforemake build,make test, andmake 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 UIAfter 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
- Create a feature branch from
main(e.g.,feature/plugin-name,fix/tab-close-crash) - Make changes
make format && make test && make lint- Push and open a pull request
Adding a Plugin
- Create
Boo/Plugins/YourPlugin/YourPlugin.swiftconforming toBooPluginProtocol - Register in
PluginRegistry.registerBuiltins() - 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 suiteLocal Development Builds
make runstripsSUFeedURLandSUPublicEDKeyfrom 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, andmake distkeep the release Sparkle configuration intact- Manual release testing should therefore use
make app,make zip, ormake dmg, notmake run
Release
Releases are automated via GitHub Actions using semantic-release with conventional commits.
make dist # Manual: release build + sign + notarize + ZIPRequires: 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:
semantic-releasecomputes the next version and updatesBoo/App/Info.plistscripts/prepare-release.shsets bothCFBundleShortVersionStringandCFBundleVersion, builds the release app bundle, signs it, optionally notarizes it, and only then packages the already-signed app into a ZIP archivescripts/update-appcast.shgenerates an appcast artifact from the freshly built ZIP archivesemantic-releasecommits the version bump- The release workflow injects the generated appcast into the documentation site artifact and deploys GitHub Pages
Conventions
TerminalColor.cgColor/.nsColorfor color conversionsAppSettings.sharedwith auto-notification for settingsRemoteExplorer.shellEscPath()for remote pathsAppSettings.shared.pluginBool/pluginString/pluginDoublefor plugin settings
