feat: Implement default welcome page for missing web application
- Added a professional HTML welcome page displayed when no web application is configured. - Enhanced `_load_webapp()` method to support improved path resolution for both development and bundled modes. - Updated error handling to show the welcome page instead of a bare error message when the webapp file is not found. - Modified unit tests to verify the welcome page is displayed in error scenarios. build: Complete Windows and macOS build scripts - Created `build_windows.py` for building Windows executable and optional MSI installer using PyInstaller. - Developed `build_macos.sh` for creating macOS application bundle and DMG image. - Added logging and error handling to build scripts for better user feedback. docs: Add build and icon requirements documentation - Created `PHASE_3_BUILD_SUMMARY.md` detailing the build process, results, and next steps. - Added `resources/icons/README.md` outlining icon requirements and creation guidelines. chore: Sync remotes script for repository maintenance - Introduced `sync_remotes.ps1` PowerShell script to fetch updates from origin and upstream remotes.
This commit is contained in:
parent
90dc09eb4d
commit
f0c96f15b8
10 changed files with 1415 additions and 39 deletions
295
build/scripts/build_macos.sh
Normal file
295
build/scripts/build_macos.sh
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
#!/bin/bash
|
||||
# Build macOS DMG package using PyInstaller
|
||||
#
|
||||
# This script builds the WebDrop Bridge application for macOS and creates
|
||||
# a distributable DMG image.
|
||||
#
|
||||
# Requirements:
|
||||
# - PyInstaller 6.0+
|
||||
# - Python 3.10+
|
||||
# - Xcode Command Line Tools (for code signing)
|
||||
# - create-dmg (optional, for custom DMG: brew install create-dmg)
|
||||
#
|
||||
# Usage:
|
||||
# bash build_macos.sh [--sign] [--notarize]
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Configuration
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
BUILD_DIR="$PROJECT_ROOT/build"
|
||||
DIST_DIR="$BUILD_DIR/dist/macos"
|
||||
TEMP_BUILD="$BUILD_DIR/temp/macos"
|
||||
SPECS_DIR="$BUILD_DIR/specs"
|
||||
SPEC_FILE="$BUILD_DIR/webdrop_bridge.spec"
|
||||
|
||||
APP_NAME="WebDropBridge"
|
||||
DMG_VOLUME_NAME="WebDrop Bridge"
|
||||
VERSION="1.0.0"
|
||||
|
||||
# Parse arguments
|
||||
SIGN_APP=0
|
||||
NOTARIZE_APP=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--sign)
|
||||
SIGN_APP=1
|
||||
shift
|
||||
;;
|
||||
--notarize)
|
||||
NOTARIZE_APP=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✅${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}⚠️${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}❌${NC} $1"
|
||||
}
|
||||
|
||||
# Main build function
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo "🚀 WebDrop Bridge macOS Build"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites
|
||||
|
||||
# Clean previous builds
|
||||
clean_builds
|
||||
|
||||
# Build with PyInstaller
|
||||
build_executable
|
||||
|
||||
# Create DMG
|
||||
create_dmg
|
||||
|
||||
# Optional: Sign and notarize
|
||||
if [ $SIGN_APP -eq 1 ]; then
|
||||
sign_app
|
||||
fi
|
||||
|
||||
if [ $NOTARIZE_APP -eq 1 ]; then
|
||||
notarize_app
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
log_success "Build completed successfully"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
log_info "Output: $DIST_DIR/"
|
||||
ls -lh "$DIST_DIR"
|
||||
}
|
||||
|
||||
check_prerequisites() {
|
||||
log_info "Checking prerequisites..."
|
||||
|
||||
# Check Python
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
log_error "Python 3 not found"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Python 3 found: $(python3 --version)"
|
||||
|
||||
# Check PyInstaller
|
||||
if ! python3 -m pip show pyinstaller &> /dev/null; then
|
||||
log_error "PyInstaller not installed. Run: pip install pyinstaller"
|
||||
exit 1
|
||||
fi
|
||||
log_success "PyInstaller installed"
|
||||
|
||||
# Check spec file
|
||||
if [ ! -f "$SPEC_FILE" ]; then
|
||||
log_error "Spec file not found: $SPEC_FILE"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Spec file found"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
clean_builds() {
|
||||
log_info "Cleaning previous builds..."
|
||||
|
||||
for dir in "$DIST_DIR" "$TEMP_BUILD" "$SPECS_DIR"; do
|
||||
if [ -d "$dir" ]; then
|
||||
rm -rf "$dir"
|
||||
log_success "Removed $dir"
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$DIST_DIR" "$TEMP_BUILD" "$SPECS_DIR"
|
||||
echo ""
|
||||
}
|
||||
|
||||
build_executable() {
|
||||
log_info "Building macOS executable with PyInstaller..."
|
||||
echo ""
|
||||
|
||||
python3 -m PyInstaller \
|
||||
--distpath="$DIST_DIR" \
|
||||
--buildpath="$TEMP_BUILD" \
|
||||
--specpath="$SPECS_DIR" \
|
||||
"$SPEC_FILE"
|
||||
|
||||
if [ ! -d "$DIST_DIR/$APP_NAME.app" ]; then
|
||||
log_error "Application bundle not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Application bundle built successfully"
|
||||
log_info "Output: $DIST_DIR/$APP_NAME.app"
|
||||
echo ""
|
||||
}
|
||||
|
||||
create_dmg() {
|
||||
log_info "Creating DMG package..."
|
||||
echo ""
|
||||
|
||||
DMG_FILE="$DIST_DIR/${APP_NAME}-${VERSION}.dmg"
|
||||
|
||||
# Remove existing DMG
|
||||
if [ -f "$DMG_FILE" ]; then
|
||||
rm -f "$DMG_FILE"
|
||||
fi
|
||||
|
||||
# Check if create-dmg is available
|
||||
if command -v create-dmg &> /dev/null; then
|
||||
log_info "Using create-dmg for professional DMG..."
|
||||
|
||||
create-dmg \
|
||||
--volname "$DMG_VOLUME_NAME" \
|
||||
--icon-size 128 \
|
||||
--window-size 512 400 \
|
||||
--app-drop-link 380 200 \
|
||||
"$DMG_FILE" \
|
||||
"$DIST_DIR/$APP_NAME.app"
|
||||
else
|
||||
log_warning "create-dmg not found, using hdiutil (less stylish)"
|
||||
log_info "For professional DMG: brew install create-dmg"
|
||||
|
||||
# Create temporary DMG directory structure
|
||||
DMG_TEMP="$TEMP_BUILD/dmg_contents"
|
||||
mkdir -p "$DMG_TEMP"
|
||||
|
||||
# Copy app bundle
|
||||
cp -r "$DIST_DIR/$APP_NAME.app" "$DMG_TEMP/"
|
||||
|
||||
# Create symlink to Applications folder
|
||||
ln -s /Applications "$DMG_TEMP/Applications"
|
||||
|
||||
# Create DMG
|
||||
hdiutil create \
|
||||
-volname "$DMG_VOLUME_NAME" \
|
||||
-srcfolder "$DMG_TEMP" \
|
||||
-ov \
|
||||
-format UDZO \
|
||||
"$DMG_FILE"
|
||||
|
||||
# Clean up
|
||||
rm -rf "$DMG_TEMP"
|
||||
fi
|
||||
|
||||
if [ ! -f "$DMG_FILE" ]; then
|
||||
log_error "DMG file not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get file size
|
||||
SIZE=$(du -h "$DMG_FILE" | cut -f1)
|
||||
log_success "DMG created successfully"
|
||||
log_info "Output: $DMG_FILE (Size: $SIZE)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
sign_app() {
|
||||
log_info "Signing application..."
|
||||
echo ""
|
||||
|
||||
# Get signing identity from environment or use ad-hoc
|
||||
SIGNING_ID="${APPLE_SIGNING_ID:--}"
|
||||
|
||||
codesign \
|
||||
--deep \
|
||||
--force \
|
||||
--verify \
|
||||
--verbose \
|
||||
--options=runtime \
|
||||
--sign "$SIGNING_ID" \
|
||||
"$DIST_DIR/$APP_NAME.app"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Code signing failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Application signed successfully"
|
||||
echo ""
|
||||
}
|
||||
|
||||
notarize_app() {
|
||||
log_info "Notarizing application..."
|
||||
echo ""
|
||||
|
||||
# Requires:
|
||||
# - APPLE_ID environment variable
|
||||
# - APPLE_PASSWORD environment variable (app-specific password)
|
||||
# - APPLE_TEAM_ID environment variable
|
||||
|
||||
if [ -z "$APPLE_ID" ] || [ -z "$APPLE_PASSWORD" ]; then
|
||||
log_error "APPLE_ID and APPLE_PASSWORD environment variables required for notarization"
|
||||
return 1
|
||||
fi
|
||||
|
||||
DMG_FILE="$DIST_DIR/${APP_NAME}-${VERSION}.dmg"
|
||||
|
||||
# Upload for notarization
|
||||
log_info "Uploading to Apple Notarization Service..."
|
||||
xcrun notarytool submit "$DMG_FILE" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$APPLE_PASSWORD" \
|
||||
--team-id "${APPLE_TEAM_ID}" \
|
||||
--wait
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Notarization failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Staple the notarization
|
||||
xcrun stapler staple "$DMG_FILE"
|
||||
|
||||
log_success "Application notarized successfully"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
Loading…
Add table
Add a link
Reference in a new issue