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:
claudi 2026-01-28 12:59:33 +01:00
parent 90dc09eb4d
commit f0c96f15b8
10 changed files with 1415 additions and 39 deletions

View 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