- Implement 'inside-out' signing strategy in package-flutter-mac-app.sh to fix nested code validity errors - Fix install_name of embedded FFI library to use @rpath for portability - Remove manual 'cargo build' triggers from Makefile and integration scripts (externalize management) - Clean up unused types and structs in Rust source (lib.rs and types.rs) - Update architecture docs to reflect AcpBridgeServerModeConfig priority logic
181 lines
6.2 KiB
Bash
Executable File
181 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
APP_DIR="$ROOT_DIR"
|
|
PUBSPEC_PATH="$ROOT_DIR/pubspec.yaml"
|
|
DIST_DIR="$ROOT_DIR/dist"
|
|
APP_NAME="${APP_NAME:-XWorkmate}"
|
|
BUILD_MODE="${BUILD_MODE:-release}"
|
|
APP_STORE_DEFINE="${APP_STORE_DEFINE:---dart-define=XWORKMATE_APP_STORE=${XWORKMATE_APP_STORE:-true}}"
|
|
SIGN_IDENTITY="${XWORKMATE_SIGN_IDENTITY:-}"
|
|
PRODUCTS_DIR_NAME="$(tr '[:lower:]' '[:upper:]' <<< "${BUILD_MODE:0:1}")${BUILD_MODE:1}"
|
|
FLUTTER_BUILD_STATE_DIR="${ROOT_DIR}/.dart_tool/flutter_build"
|
|
MACOS_BUILD_DIR="${ROOT_DIR}/build/macos"
|
|
NATIVE_ASSETS_DIR="${ROOT_DIR}/build/native_assets"
|
|
source "$ROOT_DIR/scripts/ci/apple_signing.sh"
|
|
APPLE_SIGNING_CLEANUP_COMMANDS=()
|
|
trap apple_run_cleanup EXIT
|
|
|
|
remove_tree_with_retries() {
|
|
local path="$1"
|
|
local attempts="${2:-5}"
|
|
local delay_seconds="${3:-1}"
|
|
local try=1
|
|
|
|
[[ -e "$path" ]] || return 0
|
|
|
|
while (( try <= attempts )); do
|
|
chmod -R u+w "$path" 2>/dev/null || true
|
|
rm -rf "$path" 2>/dev/null || true
|
|
|
|
if [[ ! -e "$path" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
if (( try == attempts )); then
|
|
echo "Failed to remove generated path after ${attempts} attempts: $path" >&2
|
|
return 1
|
|
fi
|
|
|
|
sleep "$delay_seconds"
|
|
((try++))
|
|
done
|
|
}
|
|
|
|
if [[ ! -f "$PUBSPEC_PATH" ]]; then
|
|
echo "Missing pubspec: $PUBSPEC_PATH" >&2
|
|
exit 1
|
|
fi
|
|
|
|
eval "$(python3 "$ROOT_DIR/scripts/ci/build_version.py" --format shell)"
|
|
BUILD_DATE_LINE="$(sed -n 's/^build-date:[[:space:]]*//p' "$PUBSPEC_PATH" | head -n 1)"
|
|
BUILD_ID_LINE="$(sed -n 's/^build-id:[[:space:]]*//p' "$PUBSPEC_PATH" | head -n 1)"
|
|
|
|
APP_VERSION="$DISPLAY_VERSION"
|
|
APP_RELEASE_VERSION="$PLATFORM_RELEASE_VERSION"
|
|
APP_BUILD="$BUILD_NUMBER"
|
|
APP_BUILD_DATE="${BUILD_DATE_LINE:-unknown}"
|
|
APP_BUILD_COMMIT="${BUILD_ID_LINE:-unknown}"
|
|
|
|
BUILD_APP_PATH="$APP_DIR/build/macos/Build/Products/$PRODUCTS_DIR_NAME/$APP_NAME.app"
|
|
DIST_APP_PATH="$DIST_DIR/$APP_NAME.app"
|
|
DIST_DMG_PATH="$DIST_DIR/$APP_NAME-$APP_VERSION.dmg"
|
|
mkdir -p "$DIST_DIR"
|
|
|
|
echo "Building $APP_NAME $APP_VERSION ($APP_BUILD) for macOS..."
|
|
# Flutter caches native-asset installation state under .dart_tool/flutter_build,
|
|
# but Xcode consumes the copied frameworks from build/native_assets/macos.
|
|
# Reset both locations so packaging cannot reuse a stale stamp or stale layout.
|
|
remove_tree_with_retries "$FLUTTER_BUILD_STATE_DIR"
|
|
remove_tree_with_retries "$MACOS_BUILD_DIR"
|
|
remove_tree_with_retries "$NATIVE_ASSETS_DIR"
|
|
|
|
if [[ -n "${APPLE_CERT_P12_BASE64:-}" &&
|
|
-n "${APPLE_CERT_PASSWORD:-}" &&
|
|
-n "${APPLE_KEYCHAIN_PASSWORD:-}" ]]; then
|
|
echo "Provisioning Apple signing certificate for macOS build..."
|
|
apple_setup_signing_keychain
|
|
else
|
|
echo "Apple signing secrets not set; using existing local macOS signing context."
|
|
fi
|
|
|
|
BUILD_ARGS=(
|
|
flutter build macos
|
|
"--$BUILD_MODE"
|
|
--build-name="$APP_RELEASE_VERSION"
|
|
--build-number="$APP_BUILD"
|
|
--dart-define="XWORKMATE_DISPLAY_VERSION=$APP_VERSION"
|
|
--dart-define="XWORKMATE_BUILD_NUMBER=$APP_BUILD"
|
|
--dart-define="XWORKMATE_BUILD_DATE=$APP_BUILD_DATE"
|
|
--dart-define="XWORKMATE_BUILD_COMMIT=$APP_BUILD_COMMIT"
|
|
"$APP_STORE_DEFINE"
|
|
)
|
|
|
|
(
|
|
cd "$APP_DIR"
|
|
"${BUILD_ARGS[@]}"
|
|
)
|
|
|
|
if [[ ! -d "$BUILD_APP_PATH" ]]; then
|
|
echo "Expected app bundle not found: $BUILD_APP_PATH" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure FFI library is embedded if it was copied to macos/Frameworks
|
|
SOURCE_FFI_LIB="$ROOT_DIR/macos/Frameworks/libcodex_ffi.dylib"
|
|
TARGET_FFI_LIB="$BUILD_APP_PATH/Contents/Frameworks/libcodex_ffi.dylib"
|
|
if [[ -f "$SOURCE_FFI_LIB" ]]; then
|
|
echo "Embedding FFI library into app bundle..."
|
|
mkdir -p "$(dirname "$TARGET_FFI_LIB")"
|
|
cp "$SOURCE_FFI_LIB" "$TARGET_FFI_LIB"
|
|
# Fix install name to be @rpath-based so it is portable within the bundle
|
|
install_name_tool -id "@rpath/$(basename "$TARGET_FFI_LIB")" "$TARGET_FFI_LIB"
|
|
fi
|
|
|
|
# Embed xworkmate-go-core for local/non-App-Store builds if available
|
|
if [[ "${XWORKMATE_APP_STORE:-}" != "true" ]]; then
|
|
SOURCE_GO_CORE="$ROOT_DIR/build/bin/xworkmate-go-core"
|
|
# lib/runtime/go_core.dart expects it under build/bin relative to one of the roots
|
|
TARGET_GO_CORE="$BUILD_APP_PATH/Contents/MacOS/build/bin/xworkmate-go-core"
|
|
if [[ -f "$SOURCE_GO_CORE" ]]; then
|
|
echo "Embedding xworkmate-go-core into app bundle..."
|
|
mkdir -p "$(dirname "$TARGET_GO_CORE")"
|
|
cp "$SOURCE_GO_CORE" "$TARGET_GO_CORE"
|
|
fi
|
|
fi
|
|
|
|
verify_bundle_signature() {
|
|
local app_path="$1"
|
|
echo "Verifying code signature: $app_path"
|
|
codesign --verify --deep --verbose=2 "$app_path"
|
|
}
|
|
|
|
validate_bundle_dependencies() {
|
|
local app_path="$1"
|
|
bash "$ROOT_DIR/scripts/validate-macos-app-bundle.sh" "$app_path"
|
|
}
|
|
|
|
echo "Validating export compliance metadata..."
|
|
bash "$ROOT_DIR/scripts/check-apple-export-compliance.sh" "$BUILD_APP_PATH"
|
|
validate_bundle_dependencies "$BUILD_APP_PATH"
|
|
|
|
rm -rf "$DIST_APP_PATH" "$DIST_DMG_PATH"
|
|
ditto "$BUILD_APP_PATH" "$DIST_APP_PATH"
|
|
|
|
echo "Re-signing app bundle to account for manual additions..."
|
|
# Components must be signed from inside out.
|
|
# 1. Sign all dylibs and frameworks
|
|
find "$DIST_APP_PATH/Contents/Frameworks" -name "*.dylib" -type f | while read -r dylib; do
|
|
echo "Signing nested dylib: $dylib"
|
|
codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$dylib"
|
|
done
|
|
|
|
find "$DIST_APP_PATH/Contents/Frameworks" -name "*.framework" -type d | while read -r framework; do
|
|
echo "Signing nested framework: $framework"
|
|
codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$framework"
|
|
done
|
|
|
|
# 2. Sign our manually added binaries if any
|
|
if [[ -f "$DIST_APP_PATH/Contents/MacOS/build/bin/xworkmate-go-core" ]]; then
|
|
echo "Signing embedded helper: xworkmate-go-core"
|
|
codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$DIST_APP_PATH/Contents/MacOS/build/bin/xworkmate-go-core"
|
|
fi
|
|
|
|
# 3. Sign the main executable
|
|
echo "Signing main executable..."
|
|
codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$DIST_APP_PATH/Contents/MacOS/$APP_NAME"
|
|
|
|
# 4. Finally sign the app bundle itself
|
|
echo "Signing app bundle..."
|
|
codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$DIST_APP_PATH"
|
|
|
|
verify_bundle_signature "$DIST_APP_PATH"
|
|
validate_bundle_dependencies "$DIST_APP_PATH"
|
|
|
|
echo "Packaging DMG..."
|
|
DMG_VOLUME_NAME="$APP_NAME" "$ROOT_DIR/scripts/create-dmg.sh" "$DIST_APP_PATH" "$DIST_DMG_PATH"
|
|
|
|
echo "App bundle: $DIST_APP_PATH"
|
|
echo "DMG: $DIST_DMG_PATH"
|