mirror of
https://github.com/token2/fido2-manage.git
synced 2026-04-09 02:35:39 +00:00
Add files via upload
This commit is contained in:
100
build_dmg.md
Normal file
100
build_dmg.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
Here’s a comprehensive **README content** for your deployment scripts, including instructions to move the `.sh` files up one level. You can tweak it for style or branding:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# FIDO2 Manager macOS Deployment Scripts
|
||||||
|
|
||||||
|
This folder contains the deployment scripts for building, packaging, and preparing the **FIDO2 Manager** application on macOS. These scripts automate the entire process from compiling the C++ CLI binary to creating a distributable DMG with the GUI.
|
||||||
|
|
||||||
|
> **Note:** These scripts are intended to be run on macOS (Apple Silicon preferred).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## ⚡ Prerequisites
|
||||||
|
|
||||||
|
Make sure you have the following installed:
|
||||||
|
|
||||||
|
* macOS (13.0 or later recommended)
|
||||||
|
* [Homebrew](https://brew.sh/)
|
||||||
|
* Xcode command line tools
|
||||||
|
* Python 3
|
||||||
|
* CMake
|
||||||
|
|
||||||
|
The deployment script will check for and install necessary Homebrew dependencies:
|
||||||
|
|
||||||
|
* pkg-config
|
||||||
|
* openssl@3
|
||||||
|
* libcbor
|
||||||
|
* zlib
|
||||||
|
* python-tk
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗 Deployment Steps
|
||||||
|
|
||||||
|
1. **These script should be in the project root**
|
||||||
|
2. **Make the main deployment script executable**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x deploy_macos.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Run the deployment script**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./deploy_macos.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
|
||||||
|
* Set up a Python virtual environment
|
||||||
|
* Build the C++ CLI (`fido2-token2`)
|
||||||
|
* Bundle required libraries
|
||||||
|
* Build the macOS GUI app with PyInstaller
|
||||||
|
* Fix library linking for macOS
|
||||||
|
* Optionally code-sign the app if `sign_macos_app.sh` is present
|
||||||
|
* Create a DMG for distribution
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Verification
|
||||||
|
|
||||||
|
After running the script:
|
||||||
|
|
||||||
|
* The final `.app` bundle will be in:
|
||||||
|
|
||||||
|
```
|
||||||
|
dist/fido2-manage.app
|
||||||
|
```
|
||||||
|
|
||||||
|
* The distributable DMG will be in:
|
||||||
|
|
||||||
|
```
|
||||||
|
dist/fido2-manage.dmg
|
||||||
|
```
|
||||||
|
|
||||||
|
* The script performs a self-contained check to ensure all required libraries are bundled and CLI commands work.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙ Customization
|
||||||
|
|
||||||
|
* **Icon:** Place your `icon.icns` in the project root to replace the placeholder icon.
|
||||||
|
* **Code signing:** If you have an Apple Developer ID, ensure `sign_macos_app.sh` exists and is executable. The deployment script will prompt to sign the app.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Notes
|
||||||
|
|
||||||
|
* Avoid spaces in your project directory path. The build process handles them but may fail in some cases.
|
||||||
|
* The script assumes ARM64 architecture. Modify `CMAKE_OSX_ARCHITECTURES` in the script if you need x86_64 support.
|
||||||
|
* If you encounter missing libraries, check the `staging` folder and ensure `bundle_libs.sh` and `fix_macos_linking.sh` exist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ Quick Tips
|
||||||
|
|
||||||
|
* To rebuild from scratch, you can safely delete `build/`, `dist/`, and `.venv/` before running the script.
|
||||||
|
* Use the final DMG to distribute the app; it contains everything needed to run on other macOS machines.
|
||||||
|
|
||||||
|
|
||||||
@@ -34,11 +34,12 @@ process_binary() {
|
|||||||
|
|
||||||
echo "${indent}Processing: $(basename "$file_to_fix")"
|
echo "${indent}Processing: $(basename "$file_to_fix")"
|
||||||
|
|
||||||
# Get the list of Homebrew library dependencies, removing trailing colons from otool output
|
# Get the list of Homebrew/local library dependencies
|
||||||
local deps=$(otool -L "$file_to_fix" 2>/dev/null | grep '/opt/homebrew/' | awk '{print $1}' | tr -d ':' | grep -v "^$file_to_fix$" || true)
|
# Matches /opt/homebrew/ (Apple Silicon) and /usr/local/ (Intel/Legacy)
|
||||||
|
local deps=$(otool -L "$file_to_fix" 2>/dev/null | grep -E '/opt/homebrew/|/usr/local/' | awk '{print $1}' | tr -d ':' | grep -v "^$file_to_fix$" || true)
|
||||||
|
|
||||||
if [[ -z "$deps" ]]; then
|
if [[ -z "$deps" ]]; then
|
||||||
echo "${indent} No Homebrew dependencies found"
|
echo "${indent} No external dependencies found"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
390
deploy_macos.sh
Normal file
390
deploy_macos.sh
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Complete deployment script for macOS FIDO2 Manager
|
||||||
|
# This script should be run on the macOS VPS after pulling latest changes
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
APP_NAME="fido2-manage"
|
||||||
|
CLI_EXECUTABLE_NAME="fido2-token2"
|
||||||
|
FINAL_APP_NAME="${APP_NAME}.app"
|
||||||
|
DMG_NAME="${APP_NAME}.dmg"
|
||||||
|
VOL_NAME="FIDO2 Manager"
|
||||||
|
BUILD_DIR="build"
|
||||||
|
DIST_DIR="dist"
|
||||||
|
STAGING_DIR="${BUILD_DIR}/staging"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Helper Functions
|
||||||
|
info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
fatal() {
|
||||||
|
echo -e "${RED}[FATAL]${NC} $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_command() {
|
||||||
|
if ! command -v "$1" &> /dev/null; then
|
||||||
|
fatal "'$1' is not installed. Please install it first."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify we're on macOS
|
||||||
|
if [[ "$OSTYPE" != "darwin"* ]]; then
|
||||||
|
fatal "This script must be run on macOS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Starting FIDO2 Manager deployment on macOS..."
|
||||||
|
|
||||||
|
# 1. Check prerequisites
|
||||||
|
info "Checking build prerequisites..."
|
||||||
|
check_command "cmake"
|
||||||
|
check_command "hdiutil"
|
||||||
|
check_command "otool"
|
||||||
|
check_command "install_name_tool"
|
||||||
|
check_command "python3"
|
||||||
|
|
||||||
|
if ! command -v "brew" &> /dev/null; then
|
||||||
|
fatal "Homebrew is not installed. Please install it first."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Install dependencies
|
||||||
|
info "Installing/checking Homebrew dependencies..."
|
||||||
|
dependencies=("pkg-config" "openssl@3" "libcbor" "zlib" "python-tk")
|
||||||
|
for dep in "${dependencies[@]}"; do
|
||||||
|
if ! brew list "$dep" &>/dev/null; then
|
||||||
|
info "Installing dependency: $dep"
|
||||||
|
brew install "$dep" || fatal "Failed to install $dep"
|
||||||
|
else
|
||||||
|
info "✓ $dep already installed"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 3. Setup Python environment
|
||||||
|
info "Setting up Python virtual environment..."
|
||||||
|
if [[ -d ".venv" ]]; then
|
||||||
|
rm -rf ".venv"
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install --upgrade pip
|
||||||
|
pip install pyinstaller
|
||||||
|
|
||||||
|
# 4. Clean old build artifacts
|
||||||
|
info "Cleaning old build artifacts..."
|
||||||
|
rm -rf "$BUILD_DIR" "$DIST_DIR"
|
||||||
|
rm -f "${APP_NAME}.spec"
|
||||||
|
|
||||||
|
# 5. Build the C++ binary
|
||||||
|
info "Building C++ binary: ${CLI_EXECUTABLE_NAME}..."
|
||||||
|
mkdir -p "$STAGING_DIR"
|
||||||
|
|
||||||
|
# Check for spaces in current directory and warn user
|
||||||
|
current_dir=$(pwd)
|
||||||
|
if [[ "$current_dir" == *" "* ]]; then
|
||||||
|
warn "Directory contains spaces: $current_dir"
|
||||||
|
warn "This may cause build issues. Consider renaming the directory."
|
||||||
|
warn "Attempting build with space-handling fixes..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set deployment target to ensure compatibility
|
||||||
|
cmake -S . -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0
|
||||||
|
cmake --build "$BUILD_DIR" --config Release
|
||||||
|
|
||||||
|
CLI_BINARY_PATH="${BUILD_DIR}/tools/${CLI_EXECUTABLE_NAME}"
|
||||||
|
if [[ ! -f "$CLI_BINARY_PATH" ]]; then
|
||||||
|
fatal "Build failed. C++ executable not found at: $CLI_BINARY_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "✓ C++ binary built successfully"
|
||||||
|
|
||||||
|
# 6. Bundle libraries and fix dependencies
|
||||||
|
info "Bundling libraries and fixing dependencies..."
|
||||||
|
if [[ ! -f "./bundle_libs.sh" ]]; then
|
||||||
|
fatal "bundle_libs.sh not found. Please ensure it exists."
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x ./bundle_libs.sh
|
||||||
|
./bundle_libs.sh "$STAGING_DIR" "$CLI_BINARY_PATH"
|
||||||
|
|
||||||
|
# 6.0.5 Copy built libfido2 library (not available via Homebrew)
|
||||||
|
info "Copying built libfido2 library..."
|
||||||
|
LIBFIDO2_BUILD_PATH="build/src/libfido2.1.15.0.dylib"
|
||||||
|
LIBFIDO2_SYMLINK_PATH="build/src/libfido2.1.dylib"
|
||||||
|
|
||||||
|
if [[ -f "$LIBFIDO2_BUILD_PATH" ]]; then
|
||||||
|
info "✓ Found built libfido2 library, copying to staging..."
|
||||||
|
cp "$LIBFIDO2_BUILD_PATH" "$STAGING_DIR/"
|
||||||
|
|
||||||
|
if [[ -L "$LIBFIDO2_SYMLINK_PATH" ]]; then
|
||||||
|
# Copy as regular file instead of symlink for better compatibility
|
||||||
|
cp "$LIBFIDO2_BUILD_PATH" "$STAGING_DIR/libfido2.1.dylib"
|
||||||
|
else
|
||||||
|
cp "$LIBFIDO2_SYMLINK_PATH" "$STAGING_DIR/" 2>/dev/null || cp "$LIBFIDO2_BUILD_PATH" "$STAGING_DIR/libfido2.1.dylib"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "✓ libfido2 library copied to staging directory"
|
||||||
|
else
|
||||||
|
warn "libfido2 library not found at: $LIBFIDO2_BUILD_PATH"
|
||||||
|
warn "App may not work on systems without libfido2 installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6.1 Fix library version compatibility
|
||||||
|
info "Fixing library version compatibility..."
|
||||||
|
cd "$STAGING_DIR"
|
||||||
|
if [[ -f "libcbor.0.12.dylib" ]] && [[ ! -f "libcbor.0.11.dylib" ]]; then
|
||||||
|
# Copy the file instead of creating symlink for better compatibility
|
||||||
|
cp libcbor.0.12.dylib libcbor.0.11.dylib
|
||||||
|
info "✓ Created libcbor version compatibility copy (safer than symlink)"
|
||||||
|
fi
|
||||||
|
# Go back to project root (staging is build/staging, so we need to go up 2 levels)
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
# 6.2 Fix library linking
|
||||||
|
info "Fixing library linking..."
|
||||||
|
info "Current directory: $(pwd)"
|
||||||
|
info "Looking for fix_macos_linking.sh..."
|
||||||
|
|
||||||
|
# Script should be in the project root
|
||||||
|
LINKING_SCRIPT="./fix_macos_linking.sh"
|
||||||
|
if [[ ! -f "$LINKING_SCRIPT" ]]; then
|
||||||
|
info "Files in current directory:"
|
||||||
|
ls -la *.sh || echo "No .sh files found"
|
||||||
|
fatal "fix_macos_linking.sh not found at: $(pwd)/$LINKING_SCRIPT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$LINKING_SCRIPT"
|
||||||
|
"$LINKING_SCRIPT"
|
||||||
|
|
||||||
|
# 7. Verify CLI functionality
|
||||||
|
info "Testing CLI functionality..."
|
||||||
|
CLI_TEST_PATH="${STAGING_DIR}/${CLI_EXECUTABLE_NAME}"
|
||||||
|
if [[ -x "$CLI_TEST_PATH" ]]; then
|
||||||
|
info "Testing CLI help..."
|
||||||
|
if "$CLI_TEST_PATH" 2>&1 | grep -q "usage:"; then
|
||||||
|
info "✓ CLI help works"
|
||||||
|
else
|
||||||
|
warn "CLI help test failed, but continuing..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Testing CLI device list..."
|
||||||
|
if "$CLI_TEST_PATH" -L &>/dev/null; then
|
||||||
|
info "✓ CLI device list works"
|
||||||
|
else
|
||||||
|
warn "CLI device list test failed (expected if no devices connected)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fatal "CLI binary is not executable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 8. Build macOS app with PyInstaller
|
||||||
|
info "Building macOS app with PyInstaller..."
|
||||||
|
|
||||||
|
# Create app icon if it doesn't exist
|
||||||
|
if [[ ! -f "icon.icns" ]]; then
|
||||||
|
info "Creating placeholder app icon..."
|
||||||
|
# Try to create a proper icon, but continue if it fails
|
||||||
|
mkdir -p icon.iconset
|
||||||
|
# Create a simple 1024x1024 PNG (you can replace this with a proper icon)
|
||||||
|
echo "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" | base64 -d > icon.iconset/icon_1024x1024.png
|
||||||
|
|
||||||
|
if command -v iconutil &> /dev/null; then
|
||||||
|
if iconutil -c icns icon.iconset 2>/dev/null; then
|
||||||
|
info "✓ Icon created successfully"
|
||||||
|
else
|
||||||
|
warn "Icon creation failed, continuing without custom icon"
|
||||||
|
rm -f icon.icns # Remove any partial icon file
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "iconutil not found, continuing without custom icon"
|
||||||
|
fi
|
||||||
|
rm -rf icon.iconset
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the app
|
||||||
|
PYINSTALLER_ARGS=(
|
||||||
|
--name "$APP_NAME"
|
||||||
|
--windowed
|
||||||
|
--noconsole
|
||||||
|
--add-data "${STAGING_DIR}/*:."
|
||||||
|
--add-data "fido2-manage-mac.sh:."
|
||||||
|
--add-binary "${STAGING_DIR}/fido2-token2:."
|
||||||
|
--osx-bundle-identifier="com.token2.fido2-manager"
|
||||||
|
--target-arch="arm64"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add icon if it exists
|
||||||
|
if [[ -f "icon.icns" ]]; then
|
||||||
|
PYINSTALLER_ARGS+=(--icon="icon.icns")
|
||||||
|
info "Using custom icon"
|
||||||
|
else
|
||||||
|
info "Building without custom icon"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pyinstaller "${PYINSTALLER_ARGS[@]}" gui-mac.py
|
||||||
|
|
||||||
|
# 9. Verify and fix app bundle
|
||||||
|
APP_BUNDLE_PATH="${DIST_DIR}/${FINAL_APP_NAME}"
|
||||||
|
if [[ ! -d "$APP_BUNDLE_PATH" ]]; then
|
||||||
|
fatal "App bundle was not created at: $APP_BUNDLE_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Verifying app bundle contents..."
|
||||||
|
BUNDLE_MACOS_DIR="${APP_BUNDLE_PATH}/Contents/MacOS"
|
||||||
|
BUNDLE_CLI_PATH="${BUNDLE_MACOS_DIR}/fido2-token2"
|
||||||
|
|
||||||
|
# Ensure CLI binary and all libraries are in MacOS directory
|
||||||
|
info "Ensuring CLI binary and libraries are in MacOS directory..."
|
||||||
|
if [[ ! -f "$BUNDLE_CLI_PATH" ]]; then
|
||||||
|
info "Copying CLI binary to app bundle MacOS directory..."
|
||||||
|
cp "${STAGING_DIR}/fido2-token2" "$BUNDLE_MACOS_DIR/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy all libraries to MacOS directory (same directory as binary)
|
||||||
|
info "Copying all libraries to MacOS directory..."
|
||||||
|
cp "${STAGING_DIR}"/*.dylib "$BUNDLE_MACOS_DIR/" 2>/dev/null || info "No additional libraries to copy"
|
||||||
|
|
||||||
|
# Copy shell script to bundle (for backward compatibility)
|
||||||
|
BUNDLE_SCRIPT_PATH="${BUNDLE_MACOS_DIR}/fido2-manage-mac.sh"
|
||||||
|
if [[ ! -f "$BUNDLE_SCRIPT_PATH" ]]; then
|
||||||
|
info "Copying macOS shell script to app bundle..."
|
||||||
|
cp "fido2-manage-mac.sh" "$BUNDLE_MACOS_DIR/"
|
||||||
|
chmod +x "$BUNDLE_SCRIPT_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set proper permissions for all executables
|
||||||
|
chmod +x "$BUNDLE_MACOS_DIR"/*
|
||||||
|
info "✓ App bundle created and verified with consistent binary placement"
|
||||||
|
|
||||||
|
# 10. Test the app bundle
|
||||||
|
info "Testing app bundle..."
|
||||||
|
if [[ -f "$BUNDLE_CLI_PATH" ]] && [[ -x "$BUNDLE_CLI_PATH" ]] && [[ -f "$BUNDLE_SCRIPT_PATH" ]] && [[ -x "$BUNDLE_SCRIPT_PATH" ]]; then
|
||||||
|
info "✓ CLI binary and shell script found in app bundle"
|
||||||
|
|
||||||
|
# Test shell script from bundle
|
||||||
|
if "$BUNDLE_SCRIPT_PATH" -help 2>&1 | grep -q "FIDO2 Token Management Tool"; then
|
||||||
|
info "✓ macOS shell script in app bundle works"
|
||||||
|
else
|
||||||
|
warn "macOS shell script in app bundle test failed"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fatal "CLI binary or shell script not found or not executable in app bundle"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 11. Test GUI (basic check)
|
||||||
|
info "Testing GUI startup..."
|
||||||
|
# This will test if the GUI can start without errors
|
||||||
|
timeout 5 python3 gui-mac.py 2>/dev/null || info "GUI test completed (expected timeout)"
|
||||||
|
|
||||||
|
# 11.5. Code sign the app (OPTIONAL - requires Apple Developer Account)
|
||||||
|
if [[ -f "./sign_macos_app.sh" ]] && [[ -x "./sign_macos_app.sh" ]]; then
|
||||||
|
warn ""
|
||||||
|
warn "Code signing script found. Do you want to sign the app?"
|
||||||
|
warn "This requires an Apple Developer ID certificate."
|
||||||
|
read -p "Sign the app? (y/N): " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
info "Code signing app bundle..."
|
||||||
|
./sign_macos_app.sh
|
||||||
|
else
|
||||||
|
warn "Skipping code signing - app may be blocked by Gatekeeper"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "No code signing script found - app will not be signed"
|
||||||
|
warn "Unsigned apps may be blocked by macOS Gatekeeper"
|
||||||
|
warn "See CODE_SIGNING_GUIDE.md for instructions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 12. Create DMG
|
||||||
|
info "Creating DMG package..."
|
||||||
|
FINAL_DMG_PATH="${DIST_DIR}/${DMG_NAME}"
|
||||||
|
if [[ -f "$FINAL_DMG_PATH" ]]; then
|
||||||
|
rm -f "$FINAL_DMG_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create temporary directory for DMG contents
|
||||||
|
DMG_TEMP_DIR=$(mktemp -d)
|
||||||
|
cp -R "$APP_BUNDLE_PATH" "$DMG_TEMP_DIR/"
|
||||||
|
ln -s /Applications "$DMG_TEMP_DIR/Applications"
|
||||||
|
|
||||||
|
# Create the DMG
|
||||||
|
hdiutil create -fs HFS+ -srcfolder "$DMG_TEMP_DIR" -volname "$VOL_NAME" "$FINAL_DMG_PATH"
|
||||||
|
rm -rf "$DMG_TEMP_DIR"
|
||||||
|
|
||||||
|
# 13. Final verification and self-contained test
|
||||||
|
info "Final verification..."
|
||||||
|
echo ""
|
||||||
|
echo "=== Build Summary ==="
|
||||||
|
echo "App bundle: $APP_BUNDLE_PATH"
|
||||||
|
echo "DMG file: $FINAL_DMG_PATH"
|
||||||
|
echo "App bundle size: $(du -sh "$APP_BUNDLE_PATH" | cut -f1)"
|
||||||
|
echo "DMG size: $(du -sh "$FINAL_DMG_PATH" | cut -f1)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== App Bundle Contents ==="
|
||||||
|
ls -la "$BUNDLE_MACOS_DIR"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Library Dependencies ==="
|
||||||
|
otool -L "$BUNDLE_CLI_PATH"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Self-Contained Verification ==="
|
||||||
|
# Test that all libraries are bundled
|
||||||
|
external_deps=$(otool -L "$BUNDLE_CLI_PATH" | grep -E '/opt/homebrew/|/usr/local/' | grep -v '@executable_path' | grep -v '@rpath' || true)
|
||||||
|
if [[ -n "$external_deps" ]]; then
|
||||||
|
warn "External dependencies found:"
|
||||||
|
echo "$external_deps"
|
||||||
|
warn "App may not work on systems without these dependencies!"
|
||||||
|
else
|
||||||
|
info "✅ All external dependencies are properly bundled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that required library files exist
|
||||||
|
echo ""
|
||||||
|
echo "=== Required Library Check ==="
|
||||||
|
FRAMEWORKS_DIR="$APP_BUNDLE_PATH/Contents/Frameworks"
|
||||||
|
required_libs=$(otool -L "$BUNDLE_CLI_PATH" | grep '@.*\.dylib' | awk '{print $1}' | sed 's/@executable_path\///g' | sed 's/@rpath\///g')
|
||||||
|
missing_libs=""
|
||||||
|
|
||||||
|
while IFS= read -r lib; do
|
||||||
|
if [[ -n "$lib" && ! -f "$FRAMEWORKS_DIR/$lib" ]]; then
|
||||||
|
missing_libs="${missing_libs}${lib}\n"
|
||||||
|
fi
|
||||||
|
done <<< "$required_libs"
|
||||||
|
|
||||||
|
if [[ -n "$missing_libs" ]]; then
|
||||||
|
warn "Missing required libraries in app bundle:"
|
||||||
|
echo -e "$missing_libs"
|
||||||
|
warn "App may fail to launch!"
|
||||||
|
else
|
||||||
|
info "✅ All required libraries are present in app bundle"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test CLI execution
|
||||||
|
echo ""
|
||||||
|
echo "=== CLI Functionality Test ==="
|
||||||
|
if "$BUNDLE_CLI_PATH" 2>&1 | head -1 | grep -q "usage:"; then
|
||||||
|
info "✅ CLI binary executes correctly"
|
||||||
|
else
|
||||||
|
warn "CLI binary test failed - may indicate linking issues"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
deactivate
|
||||||
|
|
||||||
|
info "✅ Deployment complete!"
|
||||||
|
info "Final DMG: $FINAL_DMG_PATH"
|
||||||
|
info "You can now test the app and distribute the DMG file."
|
||||||
@@ -44,32 +44,32 @@ otool -L "$BINARY_PATH"
|
|||||||
echo ""
|
echo ""
|
||||||
echo "=== Fixing Library References ==="
|
echo "=== Fixing Library References ==="
|
||||||
|
|
||||||
# Fix libcbor reference
|
# Fix all homebrew/local references dynamically
|
||||||
echo "Fixing libcbor reference..."
|
echo "Checking for external references..."
|
||||||
install_name_tool -change "/opt/homebrew/opt/libcbor/lib/libcbor.0.11.dylib" "@executable_path/libcbor.0.11.dylib" "$BINARY_PATH"
|
# capture all dependencies that look like they come from homebrew or local install
|
||||||
install_name_tool -change "/opt/homebrew/Cellar/libcbor/0.12.0/lib/libcbor.0.11.dylib" "@executable_path/libcbor.0.11.dylib" "$BINARY_PATH"
|
# Exclude system libraries (/usr/lib, /System/Library)
|
||||||
|
external_deps=$(otool -L "$BINARY_PATH" | grep -E '/opt/homebrew/|/usr/local/' | awk '{print $1}' || true)
|
||||||
|
|
||||||
# Fix OpenSSL reference
|
if [[ -n "$external_deps" ]]; then
|
||||||
echo "Fixing OpenSSL reference..."
|
echo "Found external dependencies to fix:"
|
||||||
install_name_tool -change "/opt/homebrew/opt/openssl@3/lib/libcrypto.3.dylib" "@executable_path/libcrypto.3.dylib" "$BINARY_PATH"
|
|
||||||
|
|
||||||
# Fix libfido2 @rpath reference (from local build)
|
|
||||||
echo "Fixing libfido2 @rpath reference..."
|
|
||||||
install_name_tool -change "@rpath/libfido2.1.dylib" "@executable_path/libfido2.1.dylib" "$BINARY_PATH"
|
|
||||||
|
|
||||||
# Fix any other homebrew references
|
|
||||||
echo "Checking for remaining homebrew references..."
|
|
||||||
homebrew_deps=$(otool -L "$BINARY_PATH" | grep -E '/opt/homebrew/|/usr/local/' | awk '{print $1}' || true)
|
|
||||||
|
|
||||||
if [[ -n "$homebrew_deps" ]]; then
|
|
||||||
echo "Found additional homebrew dependencies to fix:"
|
|
||||||
while IFS= read -r dep; do
|
while IFS= read -r dep; do
|
||||||
if [[ -n "$dep" ]]; then
|
if [[ -n "$dep" ]]; then
|
||||||
lib_name=$(basename "$dep")
|
lib_name=$(basename "$dep")
|
||||||
echo " Fixing: $dep -> @executable_path/$lib_name"
|
echo " Fixing: $dep -> @executable_path/$lib_name"
|
||||||
install_name_tool -change "$dep" "@executable_path/$lib_name" "$BINARY_PATH"
|
# We use || true to continue if for some reason the change fails (though it shouldn't if otool saw it)
|
||||||
|
install_name_tool -change "$dep" "@executable_path/$lib_name" "$BINARY_PATH" || echo "WARNING: Failed to change $dep"
|
||||||
fi
|
fi
|
||||||
done <<< "$homebrew_deps"
|
done <<< "$external_deps"
|
||||||
|
else
|
||||||
|
echo "No external dependencies found (or already fixed)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Special handling for libfido2 if it's linked with @rpath
|
||||||
|
# This is sometimes needed if cmake setup uses RPATH
|
||||||
|
echo "Checking for @rpath/libfido2..."
|
||||||
|
if otool -L "$BINARY_PATH" | grep -q "@rpath/libfido2"; then
|
||||||
|
echo " Fixing @rpath/libfido2 reference..."
|
||||||
|
install_name_tool -change "@rpath/libfido2.1.dylib" "@executable_path/libfido2.1.dylib" "$BINARY_PATH" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fix library IDs for the bundled libraries
|
# Fix library IDs for the bundled libraries
|
||||||
|
|||||||
104
notarize_app.sh
Normal file
104
notarize_app.sh
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Notarize the app for distribution
|
||||||
|
# Notarization is required for apps distributed outside the Mac App Store
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration - ALL MUST BE CHANGED!
|
||||||
|
APPLE_ID="your-apple-id@example.com" # Your Apple ID email
|
||||||
|
TEAM_ID="TEAMID" # Your Team ID (from developer account)
|
||||||
|
APP_PASSWORD="xxxx-xxxx-xxxx-xxxx" # App-specific password from appleid.apple.com
|
||||||
|
DMG_FILE="dist/fido2-manage.dmg"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check configuration
|
||||||
|
if [[ "$APPLE_ID" == "your-apple-id@example.com" ]]; then
|
||||||
|
error "Please configure APPLE_ID in this script!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$TEAM_ID" == "TEAMID" ]]; then
|
||||||
|
error "Please configure TEAM_ID in this script!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$APP_PASSWORD" == "xxxx-xxxx-xxxx-xxxx" ]]; then
|
||||||
|
error "Please configure APP_PASSWORD in this script!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if DMG exists
|
||||||
|
if [[ ! -f "$DMG_FILE" ]]; then
|
||||||
|
error "DMG not found at $DMG_FILE. Run ./create_signed_dmg.sh first!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Store credentials in keychain (optional but recommended)
|
||||||
|
info "Setting up notarization credentials..."
|
||||||
|
xcrun notarytool store-credentials "FIDO2_MANAGER_NOTARIZE" \
|
||||||
|
--apple-id "$APPLE_ID" \
|
||||||
|
--team-id "$TEAM_ID" \
|
||||||
|
--password "$APP_PASSWORD" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Submit for notarization
|
||||||
|
info "Submitting DMG for notarization..."
|
||||||
|
info "This may take 5-15 minutes..."
|
||||||
|
|
||||||
|
SUBMISSION_ID=$(xcrun notarytool submit "$DMG_FILE" \
|
||||||
|
--keychain-profile "FIDO2_MANAGER_NOTARIZE" \
|
||||||
|
--wait 2>&1 | grep "id:" | head -1 | awk '{print $2}')
|
||||||
|
|
||||||
|
if [[ -z "$SUBMISSION_ID" ]]; then
|
||||||
|
# Fallback to direct credentials if keychain profile fails
|
||||||
|
info "Using direct credentials..."
|
||||||
|
xcrun notarytool submit "$DMG_FILE" \
|
||||||
|
--apple-id "$APPLE_ID" \
|
||||||
|
--team-id "$TEAM_ID" \
|
||||||
|
--password "$APP_PASSWORD" \
|
||||||
|
--wait
|
||||||
|
else
|
||||||
|
info "Submission ID: $SUBMISSION_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get notarization info
|
||||||
|
info "Checking notarization status..."
|
||||||
|
xcrun notarytool info "$SUBMISSION_ID" \
|
||||||
|
--keychain-profile "FIDO2_MANAGER_NOTARIZE" 2>/dev/null || \
|
||||||
|
xcrun notarytool info "$SUBMISSION_ID" \
|
||||||
|
--apple-id "$APPLE_ID" \
|
||||||
|
--team-id "$TEAM_ID" \
|
||||||
|
--password "$APP_PASSWORD"
|
||||||
|
|
||||||
|
# Staple the notarization ticket to the DMG
|
||||||
|
info "Stapling notarization ticket to DMG..."
|
||||||
|
xcrun stapler staple "$DMG_FILE" || error "Failed to staple notarization ticket"
|
||||||
|
|
||||||
|
# Verify the stapled DMG
|
||||||
|
info "Verifying notarized DMG..."
|
||||||
|
xcrun stapler validate "$DMG_FILE" || error "Validation failed"
|
||||||
|
|
||||||
|
# Final verification
|
||||||
|
info "Running final security check..."
|
||||||
|
spctl -a -t open --context context:primary-signature -v "$DMG_FILE" || error "Security check failed"
|
||||||
|
|
||||||
|
info ""
|
||||||
|
info "✅ Notarization complete!"
|
||||||
|
info ""
|
||||||
|
info "The DMG is now ready for distribution."
|
||||||
|
info "Users can download and install without security warnings."
|
||||||
|
info ""
|
||||||
|
info "Distribution checklist:"
|
||||||
|
info "[ ] Upload to GitHub Releases"
|
||||||
|
info "[ ] Update download links"
|
||||||
|
info "[ ] Test download on clean Mac"
|
||||||
|
info "[ ] Announce release"
|
||||||
163
sign_macos_app.sh
Normal file
163
sign_macos_app.sh
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Code signing script for FIDO2 Manager
|
||||||
|
# This script must be run AFTER building but BEFORE creating the DMG
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration - MUST BE CHANGED!
|
||||||
|
DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)" # <-- CHANGE THIS!
|
||||||
|
APP_BUNDLE="dist/fido2-manage.app"
|
||||||
|
ENTITLEMENTS_FILE="entitlements.plist"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if developer ID is set
|
||||||
|
if [[ "$DEVELOPER_ID" == "Developer ID Application: Your Name (TEAMID)" ]]; then
|
||||||
|
error "Please set DEVELOPER_ID in this script first!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if app bundle exists
|
||||||
|
if [[ ! -d "$APP_BUNDLE" ]]; then
|
||||||
|
error "App bundle not found at $APP_BUNDLE. Run deploy_macos.sh first!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create entitlements file if it doesn't exist
|
||||||
|
if [[ ! -f "$ENTITLEMENTS_FILE" ]]; then
|
||||||
|
info "Creating entitlements file..."
|
||||||
|
cat > "$ENTITLEMENTS_FILE" << EOF
|
||||||
|
<?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>
|
||||||
|
<!-- Allow app to be run -->
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<false/>
|
||||||
|
|
||||||
|
<!-- Allow USB device access for FIDO2 devices -->
|
||||||
|
<key>com.apple.security.device.usb</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<!-- Allow smartcard access -->
|
||||||
|
<key>com.apple.security.smartcard</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<!-- Allow execution of unsigned code (for PyInstaller) -->
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<!-- Allow DYLD environment variables -->
|
||||||
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<!-- Disable library validation -->
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Starting code signing process..."
|
||||||
|
info "Using identity: $DEVELOPER_ID"
|
||||||
|
|
||||||
|
# Step 1: Sign all dynamic libraries
|
||||||
|
info "Signing dynamic libraries..."
|
||||||
|
find "$APP_BUNDLE" -name "*.dylib" -type f | while read -r lib; do
|
||||||
|
info " Signing: $(basename "$lib")"
|
||||||
|
codesign --force --sign "$DEVELOPER_ID" \
|
||||||
|
--timestamp \
|
||||||
|
--options runtime \
|
||||||
|
"$lib" || error "Failed to sign $(basename "$lib")"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Step 2: Sign the fido2-token2 binary
|
||||||
|
info "Signing fido2-token2 executables..."
|
||||||
|
find "$APP_BUNDLE" -name "fido2-token2" -type f | while read -r binary; do
|
||||||
|
info " Signing: $binary"
|
||||||
|
codesign --force --sign "$DEVELOPER_ID" \
|
||||||
|
--timestamp \
|
||||||
|
--options runtime \
|
||||||
|
--entitlements "$ENTITLEMENTS_FILE" \
|
||||||
|
"$binary" || error "Failed to sign fido2-token2"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Step 3: Sign shell scripts (optional but recommended)
|
||||||
|
info "Signing shell scripts..."
|
||||||
|
find "$APP_BUNDLE" -name "*.sh" -type f | while read -r script; do
|
||||||
|
info " Signing: $(basename "$script")"
|
||||||
|
codesign --force --sign "$DEVELOPER_ID" \
|
||||||
|
--timestamp \
|
||||||
|
"$script" || warn "Failed to sign $(basename "$script") - continuing anyway"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Step 4: Sign Python/PyInstaller files
|
||||||
|
info "Signing Python components..."
|
||||||
|
find "$APP_BUNDLE" -name "*.so" -type f | while read -r lib; do
|
||||||
|
info " Signing: $(basename "$lib")"
|
||||||
|
codesign --force --sign "$DEVELOPER_ID" \
|
||||||
|
--timestamp \
|
||||||
|
--options runtime \
|
||||||
|
"$lib" || error "Failed to sign $(basename "$lib")"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Step 5: Sign the main executable
|
||||||
|
info "Signing main executable..."
|
||||||
|
main_exec="$APP_BUNDLE/Contents/MacOS/fido2-manage"
|
||||||
|
if [[ -f "$main_exec" ]]; then
|
||||||
|
codesign --force --sign "$DEVELOPER_ID" \
|
||||||
|
--timestamp \
|
||||||
|
--options runtime \
|
||||||
|
--entitlements "$ENTITLEMENTS_FILE" \
|
||||||
|
"$main_exec" || error "Failed to sign main executable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 6: Sign the entire app bundle
|
||||||
|
info "Signing app bundle..."
|
||||||
|
codesign --force --deep --sign "$DEVELOPER_ID" \
|
||||||
|
--timestamp \
|
||||||
|
--options runtime \
|
||||||
|
--entitlements "$ENTITLEMENTS_FILE" \
|
||||||
|
"$APP_BUNDLE" || error "Failed to sign app bundle"
|
||||||
|
|
||||||
|
# Verify the signature
|
||||||
|
info "Verifying signature..."
|
||||||
|
if codesign --verify --deep --strict --verbose=2 "$APP_BUNDLE"; then
|
||||||
|
info "✅ App bundle signed successfully!"
|
||||||
|
else
|
||||||
|
error "❌ Signature verification failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check signature details
|
||||||
|
info ""
|
||||||
|
info "Signature details:"
|
||||||
|
codesign -dvv "$APP_BUNDLE" 2>&1 | grep -E 'Authority|TeamIdentifier|Timestamp'
|
||||||
|
|
||||||
|
# Verify entitlements
|
||||||
|
info ""
|
||||||
|
info "Entitlements summary:"
|
||||||
|
codesign -d --entitlements - "$APP_BUNDLE" 2>&1 | grep -E 'security\.(usb|smartcard|cs\.)' || true
|
||||||
|
|
||||||
|
info ""
|
||||||
|
info "✅ Code signing complete!"
|
||||||
|
info ""
|
||||||
|
info "Next steps:"
|
||||||
|
info "1. Test the app locally: open $APP_BUNDLE"
|
||||||
|
info "2. Create signed DMG: ./create_signed_dmg.sh"
|
||||||
|
info "3. Notarize the DMG for distribution: ./notarize_app.sh"
|
||||||
Reference in New Issue
Block a user