|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | + |
| 4 | +# Usage: |
| 5 | +# ./build-macos.sh |
| 6 | +# IDENTITY='Developer ID Application: ...' ./build-macos.sh |
| 7 | +# |
| 8 | +# Produces: |
| 9 | +# EntitlementJail.app |
| 10 | +# EntitlementJail.zip (ready for notarytool submit) |
| 11 | + |
| 12 | +APP_NAME="EntitlementJail" |
| 13 | +APP_BUNDLE="${APP_NAME}.app" |
| 14 | +ZIP_NAME="${APP_NAME}.zip" |
| 15 | + |
| 16 | +# Paths in this repo |
| 17 | +RUNNER_MANIFEST="runner/Cargo.toml" |
| 18 | +ENTITLEMENTS_PLIST="EntitlementJail.entitlements" |
| 19 | +INFO_PLIST_TEMPLATE="Info.plist" |
| 20 | + |
| 21 | +# Optional: embed extra payloads if present |
| 22 | +EMBED_FENCERUNNER_PATH="${EMBED_FENCERUNNER_PATH:-}" # e.g. /path/to/fencerunner |
| 23 | +EMBED_PROBES_DIR="${EMBED_PROBES_DIR:-}" # e.g. /path/to/probes |
| 24 | + |
| 25 | +# Signing identity. Prefer env override; otherwise require user to set it. |
| 26 | +IDENTITY="${IDENTITY:-}" |
| 27 | + |
| 28 | +if [[ -z "${IDENTITY}" ]]; then |
| 29 | + cat <<'EOF' 1>&2 |
| 30 | +ERROR: IDENTITY is not set. |
| 31 | +
|
| 32 | +Set it to your Developer ID Application identity string, for example: |
| 33 | + IDENTITY='Developer ID Application: Adam Hyland (42D369QV8E)' ./build-macos.sh |
| 34 | +
|
| 35 | +You can find valid identities via: |
| 36 | + security find-identity -v -p codesigning |
| 37 | +EOF |
| 38 | + exit 2 |
| 39 | +fi |
| 40 | + |
| 41 | +echo "==> Building Rust runner" |
| 42 | +cargo build --manifest-path "${RUNNER_MANIFEST}" --release |
| 43 | + |
| 44 | +# Find the built binary. (Assumes standard Cargo layout.) |
| 45 | +RUNNER_BIN="runner/target/release/runner" |
| 46 | +if [[ ! -x "${RUNNER_BIN}" ]]; then |
| 47 | + echo "ERROR: expected runner binary at ${RUNNER_BIN}" 1>&2 |
| 48 | + exit 2 |
| 49 | +fi |
| 50 | + |
| 51 | +echo "==> Assembling app bundle: ${APP_BUNDLE}" |
| 52 | +rm -rf "${APP_BUNDLE}" |
| 53 | +mkdir -p "${APP_BUNDLE}/Contents/MacOS" |
| 54 | +mkdir -p "${APP_BUNDLE}/Contents/Resources" |
| 55 | + |
| 56 | +# Copy Info.plist |
| 57 | +if [[ ! -f "${INFO_PLIST_TEMPLATE}" ]]; then |
| 58 | + echo "ERROR: missing ${INFO_PLIST_TEMPLATE} at repo root" 1>&2 |
| 59 | + exit 2 |
| 60 | +fi |
| 61 | +cp "${INFO_PLIST_TEMPLATE}" "${APP_BUNDLE}/Contents/Info.plist" |
| 62 | + |
| 63 | +# Install main executable |
| 64 | +cp "${RUNNER_BIN}" "${APP_BUNDLE}/Contents/MacOS/entitlement-jail" |
| 65 | +chmod +x "${APP_BUNDLE}/Contents/MacOS/entitlement-jail" |
| 66 | + |
| 67 | +# Optional: embed fencerunner |
| 68 | +if [[ -n "${EMBED_FENCERUNNER_PATH}" ]]; then |
| 69 | + if [[ ! -x "${EMBED_FENCERUNNER_PATH}" ]]; then |
| 70 | + echo "ERROR: EMBED_FENCERUNNER_PATH is set but not executable: ${EMBED_FENCERUNNER_PATH}" 1>&2 |
| 71 | + exit 2 |
| 72 | + fi |
| 73 | + echo "==> Embedding fencerunner: ${EMBED_FENCERUNNER_PATH}" |
| 74 | + cp "${EMBED_FENCERUNNER_PATH}" "${APP_BUNDLE}/Contents/MacOS/fencerunner" |
| 75 | + chmod +x "${APP_BUNDLE}/Contents/MacOS/fencerunner" |
| 76 | +fi |
| 77 | + |
| 78 | +# Optional: embed probes directory |
| 79 | +if [[ -n "${EMBED_PROBES_DIR}" ]]; then |
| 80 | + if [[ ! -d "${EMBED_PROBES_DIR}" ]]; then |
| 81 | + echo "ERROR: EMBED_PROBES_DIR is set but not a directory: ${EMBED_PROBES_DIR}" 1>&2 |
| 82 | + exit 2 |
| 83 | + fi |
| 84 | + echo "==> Embedding probes dir: ${EMBED_PROBES_DIR}" |
| 85 | + rsync -a --delete "${EMBED_PROBES_DIR}/" "${APP_BUNDLE}/Contents/Resources/probes/" |
| 86 | +fi |
| 87 | + |
| 88 | +# Sanity check entitlements |
| 89 | +if [[ ! -f "${ENTITLEMENTS_PLIST}" ]]; then |
| 90 | + echo "ERROR: missing entitlements plist: ${ENTITLEMENTS_PLIST}" 1>&2 |
| 91 | + exit 2 |
| 92 | +fi |
| 93 | + |
| 94 | +echo "==> Codesigning (Developer ID + hardened runtime + entitlements)" |
| 95 | +# Sign nested code first if you embed anything executable beyond the main binary. |
| 96 | +# --deep is convenient here; if you later add more embedded executables/frameworks, |
| 97 | +# consider an explicit signing pass over each nested code item. |
| 98 | +codesign \ |
| 99 | + --force \ |
| 100 | + --deep \ |
| 101 | + --options runtime \ |
| 102 | + --timestamp \ |
| 103 | + --entitlements "${ENTITLEMENTS_PLIST}" \ |
| 104 | + -s "${IDENTITY}" \ |
| 105 | + "${APP_BUNDLE}" |
| 106 | + |
| 107 | +echo "==> Verifying signature + entitlements" |
| 108 | +codesign --verify --deep --strict --verbose=2 "${APP_BUNDLE}" |
| 109 | +codesign --display --entitlements - "${APP_BUNDLE}" >/dev/null |
| 110 | + |
| 111 | +echo "==> Creating zip (for notarization): ${ZIP_NAME}" |
| 112 | +rm -f "${ZIP_NAME}" |
| 113 | +/usr/bin/ditto -c -k --keepParent "${APP_BUNDLE}" "${ZIP_NAME}" |
| 114 | + |
| 115 | +echo |
| 116 | +echo "DONE:" |
| 117 | +echo " - ${APP_BUNDLE}" |
| 118 | +echo " - ${ZIP_NAME}" |
| 119 | +echo |
| 120 | +echo "Next (notarize with your saved profile):" |
| 121 | +cat <<EOF |
| 122 | + xcrun notarytool submit "${ZIP_NAME}" --keychain-profile "dev-profile" --wait |
| 123 | + xcrun stapler staple "${APP_BUNDLE}" |
| 124 | + spctl -a -vv "${APP_BUNDLE}" |
| 125 | +EOF |
0 commit comments