Skip to main content
Xcode and xcodebuild write the built app to different directories. Cmd+R leaves it in DerivedData; the command line leaves it in build/Debug/ inside the repo. Both need the TablePro scheme and the My Mac destination.
TablePro.xcodeproj is generated from project.yml by scripts/generate-project.sh and is not in git. Run it after cloning, after editing project.yml or anything in Configs/, and after adding, moving, or deleting a source file. See Development Setup.

Debug builds

In Xcode, Cmd+B builds and Cmd+R builds and runs. From the command line:
Build and launch in one line:
-skipPackagePluginValidation belongs on every xcodebuild invocation. scripts/build-release.sh, scripts/build-plugin.sh, scripts/check-pluginkit-abi.sh and every CI job that shells out to xcodebuild pass it. Debug builds sign with an Apple Development identity resolved from TABLEPRO_DEVELOPMENT_TEAM, so signing matters even locally: a plugin in the user plugins directory has to carry the same team as the running app, and the app derives the required team from its own signature. Set the variable in Configs/Secrets.xcconfig as Development Setup describes.

Compiling every plugin

An app build compiles only the plugin bundles the app embeds, which leaves every registry-only driver uncompiled. The AllPlugins aggregate target covers all of them in one pass:
Run it after changing anything under Plugins/TableProPluginKit/, which every plugin links. To build one signed, packaged bundle instead, use scripts/build-plugin.sh <PluginTarget>; the target names are the keys under targets: in project.yml, such as OracleDriver or SurrealDBDriverPlugin.

Clean builds

The last one deletes the caches for every project on the machine, and the next build starts from nothing.

What a pull request has to pass

.github/workflows/macos-tests.yml starts on every pull request, on Xcode 26.4.1. Its first job decides whether the macOS suites run at all and skips them when nothing relevant changed, so the workflow still reports a result on a docs-only change. A plugin that builds and signs can still fail to load, which is why the load step is separate: “Bundle failed to load executable” has shipped twice. .github/workflows/repo-hygiene.yml runs beside it on Ubuntu in under a minute. It runs actionlint over every workflow, which shells out to shellcheck for each inline run: block; shellcheck at warning severity over every script; the plugin manifest check; and the shared-contract drift gates in scripts/audit-refactor-health.sh. Lint runs in the release workflow rather than this one. Run swiftlint lint --strict yourself before pushing; see Code Style. Release builds, notarization, DMG packaging and the tag that publishes them are on Releasing. Publishing a driver plugin is on Plugin Registry.