Files
openhuman/scripts/build-macos-signed.sh
T
271394ade1 Fix/skills 3 (#103)
* refactor(skills): migrate to registry-based skill management and update state handling

- Replaced Redux-based skill state management with hooks for improved performance, utilizing `useAvailableSkills`, `useSkillSnapshot`, and `useAllSkillSnapshots`.
- Streamlined skill list derivation and sorting logic to enhance clarity and maintainability.
- Updated components to reflect the new state management approach, ensuring real-time updates and compatibility with existing code.
- Bumped OpenHuman version to 0.49.24 in Cargo.lock.

* refactor(skills): update skill state handling and remove Redux dependencies

- Simplified skill state management by replacing Redux-based logic with direct references to runtime maps.
- Adjusted the SkillsGrid component to derive skill sync summary text without relying on skill states.
- Removed unused Redux configurations and tests related to skills, streamlining the codebase.

* fix(skills): make notifyOAuthComplete resilient when no local runtime

notifyOAuthComplete and triggerSync no longer throw when the frontend
SkillManager has no local runtime instance. They persist setup_complete
via RPC first, then try core RPC pass-through as fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(skills): clean up imports and improve configuration handling

- Consolidated import statements in the SkillsGrid component for better readability.
- Updated the DEV_FORCE_ONBOARDING constant in the config file to enhance clarity and maintainability by combining conditions into a single line.

* feat(skills): add SkillDebugModal for runtime skill inspection

- Introduced SkillDebugModal component to inspect a skill's runtime state, including metadata, published state, and tool definitions.
- Integrated the modal into the SkillCard component, allowing users to open it for debugging purposes.
- Implemented functionality for calling tools and displaying results, enhancing the debugging experience for skills.

* feat(skills): enhance OAuth deep link handling and skill management

- Improved the OAuth deep link process by adding steps to persist setup completion, start the skill in the core runtime, and notify the skill of OAuth completion.
- Enhanced error handling for starting skills and notifying OAuth completion, ensuring resilience in the skill management workflow.
- Updated logging throughout the process to provide better insights into the state and actions taken during OAuth handling and tool calls.

* chore(build): update Tauri configuration and add macOS build script

- Disabled the creation of updater artifacts in the Tauri configuration to streamline the build process.
- Introduced a new script for building and code-signing macOS Tauri releases, including notarization steps and environment variable validation.
- Updated environment loading logic to source from the correct secrets file for improved configuration management.

* feat(build): add pre-signing for sidecar binaries in macOS build script

- Implemented pre-signing of sidecar binaries with hardened runtime and entitlements to comply with Apple notarization requirements.
- Enhanced the build script to verify and sign all executables in the specified sidecar directory, ensuring proper code-signing before the build process.

* feat(build): implement pre-signing for sidecar binaries in macOS build script

- Added functionality to pre-sign sidecar binaries with hardened runtime and entitlements to meet Apple notarization requirements.
- Enhanced the build script to verify and sign all executables in the specified sidecar directory, ensuring compliance before the build process.

* feat(build): enhance macOS build process with notarization and sidecar re-signing

- Added a new step to the release workflow for re-signing sidecar binaries with hardened runtime and notarization after the build process.
- Updated the build script to remove pre-signing of sidecar binaries, ensuring notarization is handled separately for compliance with Apple requirements.
- Improved the overall build process by verifying and re-signing all executables within the .app bundle, including frameworks and resources, before notarization.
- Implemented DMG re-packaging after notarization to ensure the latest signed application is included in the distribution.

* refactor(capture): consolidate import statements for AppContext and WindowBounds

- Combined separate import statements for AppContext and WindowBounds into a single line for improved readability and organization in the capture module.

* refactor(logging): improve log formatting for better readability

- Updated log statements in various files to use multi-line formatting for improved clarity and consistency.
- Enhanced the SkillDebugModal and event loop logging to streamline the output and make it easier to read.
- Refactored log messages in js_handlers and ops_net to maintain uniformity in logging style.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 17:52:32 -07:00

258 lines
9.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build and codesign a macOS Tauri release (.app + .dmg).
#
# Usage:
# ./scripts/build-macos-signed.sh # release build
# ./scripts/build-macos-signed.sh --debug # debug build
# ./scripts/build-macos-signed.sh --skip-notarize # sign but skip notarization
#
# Required environment variables (or export before running):
# APPLE_CERTIFICATE_BASE64 - base64-encoded .p12 developer certificate
# APPLE_CERTIFICATE_PASSWORD - password for the .p12 certificate
# APPLE_SIGNING_IDENTITY - e.g. "Developer ID Application: Your Name (TEAMID)"
# APPLE_ID - Apple ID email for notarization
# APPLE_PASSWORD - app-specific password for notarization
# APPLE_TEAM_ID - 10-char Apple Developer team ID
#
# Optional:
# TAURI_SIGNING_PRIVATE_KEY - Tauri updater private key (for update signatures)
# TAURI_SIGNING_PRIVATE_KEY_PASSWORD - password for the updater key
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
# ── Defaults ──────────────────────────────────────────────────────────
BUILD_MODE="release"
SKIP_NOTARIZE=false
BUNDLE_TARGETS="app,dmg"
while [[ $# -gt 0 ]]; do
case "$1" in
--debug) BUILD_MODE="debug"; shift ;;
--skip-notarize) SKIP_NOTARIZE=true; shift ;;
--bundles) BUNDLE_TARGETS="$2"; shift 2 ;;
-h|--help)
sed -n '2,/^$/s/^# //p' "$0"
exit 0
;;
*) echo "Unknown flag: $1" >&2; exit 1 ;;
esac
done
# ── Load .env if present ─────────────────────────────────────────────
if [[ -f .env ]]; then
echo "Loading .env..."
set -a; source .env; set +a
fi
# Also try ci-secrets.json for local CI parity
if [[ -f scripts/ci-secrets.json ]] && command -v jq >/dev/null 2>&1; then
echo "Loading secrets from scripts/ci-secrets.json..."
eval "$(jq -r '.secrets // {} | to_entries[] | select(.value | length > 0) | "export \(.key)=\"\(.value)\""' scripts/ci-secrets.json 2>/dev/null || true)"
eval "$(jq -r '.vars // {} | to_entries[] | select(.value | length > 0) | "export \(.key)=\"\(.value)\""' scripts/ci-secrets.json 2>/dev/null || true)"
fi
# ── Validate required vars ───────────────────────────────────────────
MISSING=()
for var in APPLE_CERTIFICATE_BASE64 APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY; do
[[ -z "${!var:-}" ]] && MISSING+=("$var")
done
if ! $SKIP_NOTARIZE; then
for var in APPLE_ID APPLE_PASSWORD APPLE_TEAM_ID; do
[[ -z "${!var:-}" ]] && MISSING+=("$var")
done
fi
if [[ ${#MISSING[@]} -gt 0 ]]; then
echo "ERROR: Missing required environment variables:" >&2
printf ' %s\n' "${MISSING[@]}" >&2
echo >&2
echo "Set them in .env, scripts/ci-secrets.json, or export them before running." >&2
exit 1
fi
# ── Import certificate into a temporary keychain ─────────────────────
KEYCHAIN_NAME="build-$(date +%s).keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -base64 32)"
CERT_PATH="$(mktemp /tmp/cert-XXXXXX.p12)"
cleanup_keychain() {
echo "Cleaning up keychain..."
security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true
rm -f "$CERT_PATH"
}
trap cleanup_keychain EXIT
echo "Importing signing certificate..."
echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > "$CERT_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
security set-keychain-settings -lut 21600 "$KEYCHAIN_NAME"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
security import "$CERT_PATH" \
-k "$KEYCHAIN_NAME" \
-P "$APPLE_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign \
-T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
# Prepend build keychain so codesign finds the cert
security list-keychains -d user -s "$KEYCHAIN_NAME" $(security list-keychains -d user | tr -d '"')
echo "Verifying signing identity..."
security find-identity -v -p codesigning "$KEYCHAIN_NAME" | head -5
echo
# ── Build (signing only, no notarization) ─────────────────────────────
# We hide APPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID from Tauri so it signs
# but does NOT attempt notarization. We'll fix the sidecar signature
# and notarize ourselves afterwards.
echo "Building Tauri app (mode=$BUILD_MODE, bundles=$BUNDLE_TARGETS)..."
BUILD_ARGS=(--bundles "$BUNDLE_TARGETS")
if [[ "$BUILD_MODE" == "debug" ]]; then
BUILD_ARGS+=(--debug)
fi
# Tauri picks up signing identity from env
export APPLE_SIGNING_IDENTITY
# Save and unset notarization vars so Tauri doesn't try to notarize
_SAVED_APPLE_ID="${APPLE_ID:-}"
_SAVED_APPLE_PASSWORD="${APPLE_PASSWORD:-}"
_SAVED_APPLE_TEAM_ID="${APPLE_TEAM_ID:-}"
unset APPLE_ID APPLE_PASSWORD APPLE_TEAM_ID
env | grep -E 'APPLE|TAURI|VITE' || true
cd app
echo "Building now... ${BUILD_ARGS[@]}"
npx tauri build "${BUILD_ARGS[@]}"
echo "Done building"
cd ..
# Restore notarization vars
export APPLE_ID="$_SAVED_APPLE_ID"
export APPLE_PASSWORD="$_SAVED_APPLE_PASSWORD"
export APPLE_TEAM_ID="$_SAVED_APPLE_TEAM_ID"
# ── Locate artifacts ─────────────────────────────────────────────────
if [[ "$BUILD_MODE" == "debug" ]]; then
BUNDLE_DIR="app/src-tauri/target/debug/bundle"
else
BUNDLE_DIR="app/src-tauri/target/release/bundle"
fi
APP_PATH="$(find "$BUNDLE_DIR/macos" -name '*.app' -maxdepth 1 | head -1)"
if [[ -z "$APP_PATH" ]]; then
echo "ERROR: No .app bundle found in $BUNDLE_DIR/macos/" >&2
exit 1
fi
echo
echo "App bundle: $APP_PATH"
# ── Re-sign sidecar binaries inside the .app with hardened runtime ───
# Tauri signs sidecars during bundling but may not apply --options runtime
# or entitlements, which Apple notarization requires on ALL executables.
ENTITLEMENTS="app/src-tauri/entitlements.sidecar.plist"
echo
echo "Re-signing binaries inside the .app with hardened runtime..."
# Sign all executables in Contents/MacOS except the main app binary
MAIN_EXECUTABLE="$(defaults read "$APP_PATH/Contents/Info.plist" CFBundleExecutable 2>/dev/null || echo "OpenHuman")"
for bin in "$APP_PATH/Contents/MacOS/"*; do
[[ -f "$bin" && -x "$bin" ]] || continue
BASENAME="$(basename "$bin")"
if [[ "$BASENAME" == "$MAIN_EXECUTABLE" ]]; then
continue # main binary — will be re-signed with the whole .app
fi
echo " Re-signing sidecar: $BASENAME"
codesign --force --options runtime \
--entitlements "$ENTITLEMENTS" \
--sign "$APPLE_SIGNING_IDENTITY" \
--timestamp \
"$bin"
codesign --verify --strict --verbose=1 "$bin"
done
# Also sign any frameworks/dylibs inside the bundle
for lib in "$APP_PATH/Contents/Frameworks/"*.dylib "$APP_PATH/Contents/Frameworks/"*.framework; do
[[ -e "$lib" ]] || continue
echo " Re-signing framework: $(basename "$lib")"
codesign --force --options runtime \
--sign "$APPLE_SIGNING_IDENTITY" \
--timestamp \
"$lib"
done
# Re-sign the entire .app so the seal covers the updated sidecar signatures
echo " Re-signing .app bundle..."
codesign --force --options runtime \
--entitlements "$ENTITLEMENTS" \
--sign "$APPLE_SIGNING_IDENTITY" \
--timestamp \
"$APP_PATH"
echo
echo "Verifying code signature..."
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
echo "Signature OK."
# Verify sidecar specifically
SIDECAR="$(find "$APP_PATH/Contents/MacOS" -name 'openhuman*' ! -name "$MAIN_EXECUTABLE" 2>/dev/null | head -1)"
if [[ -n "$SIDECAR" ]]; then
echo "Verifying sidecar hardened runtime..."
codesign -d --verbose=4 "$SIDECAR" 2>&1 | grep -E 'flags|runtime' || true
fi
# ── Notarize ──────────────────────────────────────────────────────────
if $SKIP_NOTARIZE; then
echo
echo "Skipping notarization (--skip-notarize)."
else
NOTARIZE_FILE="$(mktemp /tmp/OpenHuman-XXXXXX.zip)"
echo
echo "Creating zip for notarization..."
ditto -c -k --keepParent "$APP_PATH" "$NOTARIZE_FILE"
echo "Submitting for notarization..."
xcrun notarytool submit "$NOTARIZE_FILE" \
--apple-id "$APPLE_ID" \
--password "$APPLE_PASSWORD" \
--team-id "$APPLE_TEAM_ID" \
--wait
rm -f "$NOTARIZE_FILE"
echo
echo "Stapling notarization ticket..."
xcrun stapler staple "$APP_PATH"
# Re-create DMG after stapling if dmg was in bundle targets
DMG_PATH="$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -maxdepth 1 2>/dev/null | head -1)"
if [[ -n "$DMG_PATH" ]]; then
echo "Re-creating DMG with stapled .app..."
DMG_TEMP="$(mktemp /tmp/OpenHuman-XXXXXX.dmg)"
hdiutil create -volname "OpenHuman" -srcfolder "$APP_PATH" -ov -format UDZO "$DMG_TEMP"
mv "$DMG_TEMP" "$DMG_PATH"
xcrun stapler staple "$DMG_PATH"
fi
echo "Notarization complete."
fi
# ── Summary ───────────────────────────────────────────────────────────
echo
echo "===== Build complete ====="
echo " App: $APP_PATH"
[[ -n "$DMG_PATH" ]] && echo " DMG: $DMG_PATH"
echo
echo "To install:"
echo " cp -R \"$APP_PATH\" /Applications/"
echo " # or open \"$DMG_PATH\""