← Files Build macOS AppsARCHIVED FILE

skills/build-run-debug/references/run-button-bootstrap.md

4.64 KB · Oct 3, 2026 · 06:38 UTC

↓ Download file

# Run Button Bootstrap

This is the canonical bootstrap contract for the macOS Build plugin's local run
loop.

When a project does not already have an established macOS run entrypoint:

1. Create one project-local `script/build_and_run.sh`.
2. Make it executable.
3. Use it as the single kill + build + run entrypoint.
4. Support optional `--debug`, `--logs`, `--telemetry`, and `--verify` flags.
5. Write `.codex/environments/environment.toml` so the Codex app exposes a
   `Run` action wired to that script.

## `script/build_and_run.sh`

Use one project-specific script with a tiny mode switch and a default no-flag
path that just kills, builds, and launches. Keep raw executable launch only for
true command-line tools. For SwiftPM AppKit/SwiftUI GUI apps, stage a
project-local `.app` bundle and launch that bundle with `/usr/bin/open -n`.

### SwiftPM CLI executable

Use this shape for true command-line tools:

```bash
#!/usr/bin/env bash
set -euo pipefail

MODE="${1:-run}"
APP_NAME="MyTool"

pkill -x "$APP_NAME" >/dev/null 2>&1 || true

swift build
APP_BINARY="$(swift build --show-bin-path)/$APP_NAME"

case "$MODE" in
  run)
    "$APP_BINARY"
    ;;
  --debug|debug)
    lldb -- "$APP_BINARY"
    ;;
  --logs|logs)
    "$APP_BINARY" &
    /usr/bin/log stream --info --style compact --predicate "process == \"$APP_NAME\""
    ;;
  --telemetry|telemetry)
    "$APP_BINARY" &
    /usr/bin/log stream --info --style compact --predicate "subsystem == \"com.example.MyTool\""
    ;;
  --verify|verify)
    "$APP_BINARY" &
    sleep 1
    pgrep -x "$APP_NAME" >/dev/null
    ;;
  *)
    echo "usage: $0 [run|--debug|--logs|--telemetry|--verify]" >&2
    exit 2
    ;;
esac
```

### SwiftPM AppKit/SwiftUI GUI app

Use this shape for SwiftPM GUI apps so they launch as a real foreground app
bundle with Dock activation and bundle metadata:

```bash
#!/usr/bin/env bash
set -euo pipefail

MODE="${1:-run}"
APP_NAME="MyApp"
BUNDLE_ID="com.example.MyApp"
MIN_SYSTEM_VERSION="14.0"

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DIST_DIR="$ROOT_DIR/dist"
APP_BUNDLE="$DIST_DIR/$APP_NAME.app"
APP_CONTENTS="$APP_BUNDLE/Contents"
APP_MACOS="$APP_CONTENTS/MacOS"
APP_BINARY="$APP_MACOS/$APP_NAME"
INFO_PLIST="$APP_CONTENTS/Info.plist"

pkill -x "$APP_NAME" >/dev/null 2>&1 || true

swift build
BUILD_BINARY="$(swift build --show-bin-path)/$APP_NAME"

rm -rf "$APP_BUNDLE"
mkdir -p "$APP_MACOS"
cp "$BUILD_BINARY" "$APP_BINARY"
chmod +x "$APP_BINARY"

cat >"$INFO_PLIST" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleExecutable</key>
  <string>$APP_NAME</string>
  <key>CFBundleIdentifier</key>
  <string>$BUNDLE_ID</string>
  <key>CFBundleName</key>
  <string>$APP_NAME</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>LSMinimumSystemVersion</key>
  <string>$MIN_SYSTEM_VERSION</string>
  <key>NSPrincipalClass</key>
  <string>NSApplication</string>
</dict>
</plist>
PLIST

open_app() {
  /usr/bin/open -n "$APP_BUNDLE"
}

case "$MODE" in
  run)
    open_app
    ;;
  --debug|debug)
    lldb -- "$APP_BINARY"
    ;;
  --logs|logs)
    open_app
    /usr/bin/log stream --info --style compact --predicate "process == \"$APP_NAME\""
    ;;
  --telemetry|telemetry)
    open_app
    /usr/bin/log stream --info --style compact --predicate "subsystem == \"$BUNDLE_ID\""
    ;;
  --verify|verify)
    open_app
    sleep 1
    pgrep -x "$APP_NAME" >/dev/null
    ;;
  *)
    echo "usage: $0 [run|--debug|--logs|--telemetry|--verify]" >&2
    exit 2
    ;;
esac
```

Launching a SwiftPM GUI binary directly can produce no Dock icon, no foreground
activation, and missing bundle identifier warnings. If the `.app` bundle opens
but the main window still does not come forward, the app entrypoint may need
`NSApp.setActivationPolicy(.regular)` and
`NSApp.activate(ignoringOtherApps: true)`.

Adapt the build step for Xcode projects by replacing `swift build` with
`xcodebuild -project ...` or `xcodebuild -workspace ...`, then launch the built
`.app` binary from DerivedData or a deterministic project-local build path. Keep
the one-script interface and mode flags the same.

## `.codex/environments/environment.toml`

Write the environment file at this exact path:

`.codex/environments/environment.toml`

with this action shape:

```toml
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "<project-name>"

[setup]
script = ""

[[actions]]
name = "Run"
icon = "run"
command = "./script/build_and_run.sh"
```

If the project already has an environment file, update the existing `Run`
action to point at `./script/build_and_run.sh` instead of adding a duplicate.

SHA-256: 927483096a5610c731b1bb49a106e0d4453fdb08cb872c299b86c8370ae856a7