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:
-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. TheAllPlugins aggregate target covers all of them in one pass:
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.

