From 00f1f18abbd6bce4528df6720af4498d5d2f4555 Mon Sep 17 00:00:00 2001 From: Dries Peeters Date: Sun, 11 Jan 2026 20:51:13 +0100 Subject: [PATCH] Improve build scripts and CI/CD workflows - Enhance build scripts with better error handling and Windows support - Add Windows-specific build scripts and PowerShell utilities - Add asset preparation and verification scripts - Update GitHub Actions workflows for desktop and mobile builds - Add build configuration and troubleshooting utilities - Improve cross-platform build compatibility --- .github/workflows/build-desktop.yml | 13 +- .github/workflows/build-mobile.yml | 2 +- scripts/build-all.bat | 38 ++- scripts/build-all.sh | 131 ++++++++- scripts/build-desktop-no-sign.bat | 70 +++++ scripts/build-desktop-no-sign.sh | 114 ++++++++ scripts/build-desktop-simple.bat | 125 +++++++++ scripts/build-desktop-windows.bat | 141 ++++++++++ scripts/build-desktop-windows.ps1 | 134 +++++++++ scripts/build-desktop.bat | 123 ++++++++- scripts/build-desktop.sh | 411 ++++++++++++++++++++++++++-- scripts/build-mobile.bat | 11 + scripts/build-mobile.sh | 11 + scripts/fix-onedrive-lock.ps1 | 136 +++++++++ scripts/generate-icons.js | 128 +++++++++ scripts/sync-desktop-version.py | 84 ++++++ scripts/sync-mobile-version.py | 152 ++++++++++ 17 files changed, 1766 insertions(+), 58 deletions(-) create mode 100644 scripts/build-desktop-no-sign.bat create mode 100644 scripts/build-desktop-no-sign.sh create mode 100644 scripts/build-desktop-simple.bat create mode 100644 scripts/build-desktop-windows.bat create mode 100644 scripts/build-desktop-windows.ps1 create mode 100644 scripts/fix-onedrive-lock.ps1 create mode 100644 scripts/generate-icons.js create mode 100644 scripts/sync-desktop-version.py create mode 100644 scripts/sync-mobile-version.py diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 4a8dd77a..f0fa7a50 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -29,10 +29,14 @@ jobs: - name: Build Windows working-directory: desktop + env: + # Code signing (optional - only signs if secrets are configured) + CSC_LINK: ${{ secrets.WINDOWS_CODE_SIGN_CERT }} + CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CODE_SIGN_PASSWORD }} run: npm run build:win - name: Upload Windows installer - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: windows-installer path: desktop/dist/*.exe @@ -56,10 +60,11 @@ jobs: run: npm run build:linux - name: Upload Linux packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux-packages - path: desktop/dist/*.AppImage + path: | + desktop/dist/*.AppImage desktop/dist/*.deb build-macos: @@ -81,7 +86,7 @@ jobs: run: npm run build:mac - name: Upload macOS DMG - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: macos-dmg path: desktop/dist/*.dmg diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index ab585374..3308fa91 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -43,7 +43,7 @@ jobs: run: flutter build apk --release - name: Upload APK - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: android-apk path: mobile/build/app/outputs/flutter-apk/app-release.apk diff --git a/scripts/build-all.bat b/scripts/build-all.bat index 811d85fa..6e622bd0 100644 --- a/scripts/build-all.bat +++ b/scripts/build-all.bat @@ -101,9 +101,20 @@ if "%BUILD_MOBILE%"=="1" ( echo ======================================== echo Building Mobile App (Flutter) echo ======================================== + cd /d "%PROJECT_ROOT%" + + echo [0/6] Syncing version from setup.py... + python "%SCRIPT_DIR%sync-mobile-version.py" + if errorlevel 1 ( + echo [ERROR] Failed to sync version + exit /b 1 + ) + echo [OK] Version synced + echo. + cd /d "%PROJECT_ROOT%\mobile" - echo [1/5] Installing Flutter dependencies... + echo [1/6] Installing Flutter dependencies... call flutter pub get if errorlevel 1 ( echo [ERROR] Failed to install Flutter dependencies @@ -112,14 +123,14 @@ if "%BUILD_MOBILE%"=="1" ( echo [OK] Dependencies installed echo. - echo [2/5] Analyzing Flutter code... + echo [2/6] Analyzing Flutter code... call flutter analyze if errorlevel 1 ( echo [WARNING] Code analysis found issues (continuing anyway) ) echo. - echo [3/5] Running Flutter tests... + echo [3/6] Running Flutter tests... call flutter test if errorlevel 1 ( echo [WARNING] Some tests failed (continuing anyway) @@ -127,7 +138,7 @@ if "%BUILD_MOBILE%"=="1" ( echo. if "%BUILD_ANDROID%"=="1" ( - echo [4/5] Building Android APK... + echo [4/6] Building Android APK... call flutter build apk --release if errorlevel 1 ( echo [ERROR] Android APK build failed @@ -141,7 +152,7 @@ if "%BUILD_MOBILE%"=="1" ( ) echo. - echo [5/5] Building Android App Bundle... + echo [5/6] Building Android App Bundle... call flutter build appbundle --release if errorlevel 1 ( echo [ERROR] Android App Bundle build failed @@ -162,9 +173,20 @@ if "%BUILD_DESKTOP%"=="1" ( echo ======================================== echo Building Desktop App (Electron) echo ======================================== + cd /d "%PROJECT_ROOT%" + + echo [0/4] Syncing version from setup.py... + python "%SCRIPT_DIR%sync-desktop-version.py" + if errorlevel 1 ( + echo [ERROR] Failed to sync version + exit /b 1 + ) + echo [OK] Version synced + echo. + cd /d "%PROJECT_ROOT%\desktop" - echo [1/3] Installing npm dependencies... + echo [1/4] Installing npm dependencies... if not exist "node_modules" ( call npm install --prefer-offline --no-audit --loglevel=warn ) else ( @@ -185,7 +207,7 @@ if "%BUILD_DESKTOP%"=="1" ( if "%1"=="all-desktop" goto build_all_platforms if "%BUILD_WINDOWS%"=="1" ( - echo [2/3] Building Windows installer... + echo [2/4] Building Windows installer... call npm run build:win if errorlevel 1 ( echo [ERROR] Windows build failed @@ -203,7 +225,7 @@ if "%BUILD_DESKTOP%"=="1" ( goto end_desktop_build :build_all_platforms - echo [2/3] Building for all supported platforms... + echo [2/4] Building for all supported platforms... echo [NOTE] Will automatically build for platforms supported on your OS call npm run build:all if errorlevel 1 ( diff --git a/scripts/build-all.sh b/scripts/build-all.sh index 1d950b39..d8255595 100644 --- a/scripts/build-all.sh +++ b/scripts/build-all.sh @@ -64,6 +64,18 @@ check_npm() { build_mobile() { print_header "Building Mobile App (Flutter)" + cd "$PROJECT_ROOT" + + # Sync version from setup.py to mobile app + print_header "Syncing version from setup.py" + python3 "$SCRIPT_DIR/sync-mobile-version.py" + if [ $? -ne 0 ]; then + print_error "Failed to sync version" + exit 1 + fi + print_success "Version synced" + echo "" + cd "$PROJECT_ROOT/mobile" # Check if Flutter is available @@ -127,18 +139,82 @@ build_mobile() { build_desktop() { print_header "Building Desktop App (Electron)" + cd "$PROJECT_ROOT" + + # Sync version from setup.py to package.json + print_header "Syncing version from setup.py" + python3 "$SCRIPT_DIR/sync-desktop-version.py" + if [ $? -ne 0 ]; then + print_error "Failed to sync version" + exit 1 + fi + print_success "Version synced" + echo "" + cd "$PROJECT_ROOT/desktop" + # Prepare assets (logo, icons) - only once + ASSETS_CHECK_FILE="$PROJECT_ROOT/desktop/.assets_prepared" + if [ ! -f "$ASSETS_CHECK_FILE" ]; then + print_header "Preparing desktop assets" + bash "$SCRIPT_DIR/prepare-desktop-assets.sh" || { + print_warning "Asset preparation had issues, continuing anyway..." + } + touch "$ASSETS_CHECK_FILE" + echo "" + fi + # Check prerequisites check_node check_npm # Install dependencies print_header "Installing npm dependencies" - if [ ! -d "node_modules" ]; then - npm install --prefer-offline --no-audit --loglevel=warn - else - npm ci --prefer-offline --no-audit --loglevel=warn || npm install --prefer-offline --no-audit --loglevel=warn + + # Check if node_modules is valid + NODE_MODULES_VALID=false + if [ -d "node_modules" ] && [ -f "node_modules/.package-lock.json" ] || [ -f "package-lock.json" ]; then + if node -e "require('electron')" 2>/dev/null; then + NODE_MODULES_VALID=true + print_success "node_modules appears valid, skipping install" + fi + fi + + if [ "$NODE_MODULES_VALID" = false ]; then + # Detect Windows/OneDrive + IS_WINDOWS=false + IS_ONEDRIVE=false + if [[ "$(uname -s)" == MINGW* ]] || [[ "$(uname -s)" == MSYS* ]] || [[ "$(uname -s)" == CYGWIN* ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then + IS_WINDOWS=true + fi + if [[ "$PROJECT_ROOT" == *"OneDrive"* ]]; then + IS_ONEDRIVE=true + fi + + if [ ! -d "node_modules" ]; then + npm install --prefer-offline --no-audit --loglevel=warn || { + if [ "$IS_WINDOWS" = true ] || [ "$IS_ONEDRIVE" = true ]; then + print_error "npm install failed - Windows/OneDrive issue detected" + echo " Solutions: Exclude node_modules from OneDrive, run as Admin, or move project" + else + print_error "npm install failed" + fi + exit 1 + } + else + npm ci --prefer-offline --no-audit --loglevel=warn || { + print_warning "npm ci failed, trying npm install..." + npm install --prefer-offline --no-audit --loglevel=warn || { + if [ "$IS_WINDOWS" = true ] || [ "$IS_ONEDRIVE" = true ]; then + print_error "npm install failed - Windows/OneDrive issue" + echo " Try: Exclude node_modules from OneDrive sync" + else + print_error "npm install failed" + fi + exit 1 + } + } + fi fi # Run tests (if available) @@ -154,15 +230,45 @@ build_desktop() { if [ "$1" = "all-platforms" ] || [ "$1" = "all-desktop" ]; then print_header "Building for all supported platforms" print_warning "Note: Will automatically build for platforms supported on your OS" - npm run build:all + npx electron-builder --win --mac --linux return 0 fi - case "$PLATFORM" in - Linux*) + # Detect platform more accurately + detect_platform() { + local os=$(uname -s) + if [[ "$os" == MINGW* ]] || [[ "$os" == MSYS* ]] || [[ "$os" == CYGWIN* ]]; then + echo "win32" + elif [[ "$os" == Linux* ]]; then + echo "linux" + elif [[ "$os" == Darwin* ]]; then + echo "darwin" + else + echo "unknown" + fi + } + + DETECTED_PLATFORM=$(detect_platform) + + case "$DETECTED_PLATFORM" in + win32) + if [ "$BUILD_LINUX" = "true" ] || [ "$1" = "linux" ] || [ "$1" = "all" ] || [ -z "$1" ]; then + print_header "Building Windows installer" + npx electron-builder --win + + if [ -d "dist" ] && [ "$(ls -A dist/*.exe dist/*.nsis 2>/dev/null)" ]; then + print_success "Windows installer built successfully" + echo " Location: $PROJECT_ROOT/desktop/dist/" + ls -lh dist/*.exe dist/*.nsis 2>/dev/null || true + else + print_error "Windows build failed" + fi + fi + ;; + linux) if [ "$BUILD_LINUX" = "true" ] || [ "$1" = "linux" ] || [ "$1" = "all" ] || [ -z "$1" ]; then print_header "Building Linux packages" - npm run build:linux + npx electron-builder --linux if [ -d "dist" ] && [ "$(ls -A dist/*.AppImage dist/*.deb 2>/dev/null)" ]; then print_success "Linux packages built successfully" @@ -173,10 +279,10 @@ build_desktop() { fi fi ;; - Darwin*) + darwin) if [ "$BUILD_MACOS" = "true" ] || [ "$1" = "macos" ] || [ "$1" = "all" ] || [ -z "$1" ]; then print_header "Building macOS DMG" - npm run build:mac + npx electron-builder --mac if [ -d "dist" ] && [ -f "dist"/*.dmg ]; then print_success "macOS DMG built successfully" @@ -187,11 +293,10 @@ build_desktop() { fi fi ;; - MINGW*|MSYS*|CYGWIN*) - print_warning "Use build-all.bat for Windows builds" - ;; *) print_warning "Unknown platform: $PLATFORM" + print_warning "Attempting Windows build..." + npx electron-builder --win ;; esac } diff --git a/scripts/build-desktop-no-sign.bat b/scripts/build-desktop-no-sign.bat new file mode 100644 index 00000000..dee36888 --- /dev/null +++ b/scripts/build-desktop-no-sign.bat @@ -0,0 +1,70 @@ +@echo off +REM Build script that completely prevents code signing and winCodeSign download + +setlocal enabledelayedexpansion +set SCRIPT_DIR=%~dp0 +set PROJECT_ROOT=%SCRIPT_DIR%.. +set DESKTOP_DIR=%PROJECT_ROOT%\desktop + +cd /d "%PROJECT_ROOT%" + +REM Set ALL code signing environment variables +set CSC_IDENTITY_AUTO_DISCOVERY=false +set WIN_CSC_LINK= +set WIN_CSC_KEY_PASSWORD= +set CSC_LINK= +set CSC_KEY_PASSWORD= +set APPLE_ID= +set APPLE_ID_PASSWORD= +set APPLE_TEAM_ID= + +echo Building TimeTracker Desktop App (NO CODE SIGNING)... +echo. + +REM Clear ALL electron-builder cache +echo Clearing electron-builder cache completely... +if exist "%LOCALAPPDATA%\electron-builder" ( + echo Removing: %LOCALAPPDATA%\electron-builder + rmdir /s /q "%LOCALAPPDATA%\electron-builder" 2>nul || ( + echo [WARNING] Could not remove cache (may need Admin rights) + echo Manually delete: %LOCALAPPDATA%\electron-builder + ) +) +echo. + +echo Environment variables set: +echo CSC_IDENTITY_AUTO_DISCOVERY=false +echo WIN_CSC_LINK= +echo CSC_LINK= +echo. + +cd /d "%DESKTOP_DIR%" + +echo Building with explicit no-sign configuration... +echo Using explicit CLI flags to disable code signing... +echo. + +REM Build with environment variables only (package.json already has sign: null) +CSC_IDENTITY_AUTO_DISCOVERY=false WIN_CSC_LINK= CSC_LINK= call npx --yes electron-builder --win + +if errorlevel 1 ( + echo. + echo ================================================================================ + echo BUILD FAILED + echo ================================================================================ + echo. + echo If you got a symlink error: + echo 1. Enable Developer Mode: Win+I ^> Privacy ^& Security ^> For developers + echo 2. Restart your terminal + echo 3. Try building again + echo. + echo Or run as Administrator + echo. + exit /b 1 +) + +echo. +echo [SUCCESS] Build completed without code signing! +echo. + +endlocal diff --git a/scripts/build-desktop-no-sign.sh b/scripts/build-desktop-no-sign.sh new file mode 100644 index 00000000..88d91491 --- /dev/null +++ b/scripts/build-desktop-no-sign.sh @@ -0,0 +1,114 @@ +#!/bin/bash +# Build script that completely prevents code signing and winCodeSign download + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +DESKTOP_DIR="$PROJECT_ROOT/desktop" + +cd "$PROJECT_ROOT" + +# Function to detect platform +detect_platform() { + local uname_out=$(uname -s) + case "${uname_out}" in + Linux*) echo "linux" ;; + Darwin*) echo "darwin" ;; + MINGW*|MSYS*|CYGWIN*) + echo "win32" ;; + *) echo "unknown" ;; + esac +} + +PLATFORM=$(detect_platform) + +echo "Building TimeTracker Desktop App (NO CODE SIGNING)..." +echo "" + +# Set ALL code signing environment variables to prevent downloads +export CSC_IDENTITY_AUTO_DISCOVERY=false +export WIN_CSC_LINK="" +export WIN_CSC_KEY_PASSWORD="" +export CSC_LINK="" +export CSC_KEY_PASSWORD="" +export APPLE_ID="" +export APPLE_ID_PASSWORD="" +export APPLE_TEAM_ID="" + +# Clear ALL electron-builder cache +echo "Clearing electron-builder cache completely..." +CACHE_BASE="" +if [ "$PLATFORM" = "win32" ]; then + if [ -n "$LOCALAPPDATA" ]; then + CACHE_BASE="$LOCALAPPDATA/electron-builder" + elif [ -n "$HOME" ]; then + CACHE_BASE="$HOME/AppData/Local/electron-builder" + fi +else + if [ "$PLATFORM" = "darwin" ]; then + CACHE_BASE="$HOME/Library/Caches/electron-builder" + else + CACHE_BASE="$HOME/.cache/electron-builder" + fi +fi + +if [ -n "$CACHE_BASE" ] && [ -d "$CACHE_BASE" ]; then + echo "Removing: $CACHE_BASE" + rm -rf "$CACHE_BASE" 2>/dev/null || { + echo "[WARNING] Could not remove cache (may need Admin rights)" + echo " Manually delete: $CACHE_BASE" + } +fi + +echo "" +echo "Environment variables set:" +echo " CSC_IDENTITY_AUTO_DISCOVERY=false" +echo " WIN_CSC_LINK=''" +echo " CSC_LINK=''" +echo "" + +cd "$DESKTOP_DIR" + +# Build with explicit no-sign configuration using CLI flags only (no temp config) +echo "Building with explicit no-sign configuration..." +echo "" + +case "$PLATFORM" in + win32) + echo "Building Windows installer..." + echo "Using explicit CLI flags to disable code signing..." + CSC_IDENTITY_AUTO_DISCOVERY=false \ + WIN_CSC_LINK="" \ + CSC_LINK="" \ + npx --yes electron-builder --win + ;; + darwin) + echo "Building macOS installer..." + echo "Using explicit CLI flags to disable code signing..." + CSC_IDENTITY_AUTO_DISCOVERY=false \ + CSC_LINK="" \ + APPLE_ID="" \ + APPLE_ID_PASSWORD="" \ + npx --yes electron-builder --mac --config.mac.sign=null + ;; + linux) + echo "Building Linux installer..." + echo "Linux doesn't require code signing configuration..." + CSC_IDENTITY_AUTO_DISCOVERY=false \ + npx --yes electron-builder --linux + ;; + *) + echo "ERROR: Unknown platform: $PLATFORM" + exit 1 + ;; +esac + +if [ $? -eq 0 ]; then + echo "" + echo "[SUCCESS] Build completed without code signing!" +else + echo "" + echo "[ERROR] Build failed" + exit 1 +fi diff --git a/scripts/build-desktop-simple.bat b/scripts/build-desktop-simple.bat new file mode 100644 index 00000000..552521f0 --- /dev/null +++ b/scripts/build-desktop-simple.bat @@ -0,0 +1,125 @@ +@echo off +REM Simplified build script for TimeTracker Desktop App (Windows) +REM This version uses simpler logic that's more reliable + +setlocal +set SCRIPT_DIR=%~dp0 +set PROJECT_ROOT=%SCRIPT_DIR%.. +set DESKTOP_DIR=%PROJECT_ROOT%\desktop + +cd /d "%PROJECT_ROOT%" + +REM Sync version +echo Syncing version from setup.py... +python "%SCRIPT_DIR%sync-desktop-version.py" +if errorlevel 1 ( + echo ERROR: Failed to sync version + exit /b 1 +) +echo. + +cd /d "%DESKTOP_DIR%" +echo Building TimeTracker Desktop App... +echo. + +REM Check Node.js +where node >nul 2>&1 +if errorlevel 1 ( + echo ERROR: Node.js not found + exit /b 1 +) + +node --version +npm --version +echo. + +REM Check if we need to install dependencies +echo Checking dependencies... +if not exist "node_modules\electron-builder\package.json" ( + echo Installing dependencies... + if not exist "node_modules" ( + echo First time install... + ) else ( + echo Updating existing installation... + echo (If this fails with EPERM error, see ONEDRIVE_FIX.md) + ) + call npm install --prefer-offline --no-audit --loglevel=warn + if errorlevel 1 ( + echo. + echo ======================================== + echo ERROR: npm install failed! + echo ======================================== + echo. + echo This is likely a OneDrive file locking issue. + echo. + echo QUICK FIX: + echo 1. Right-click: desktop\node_modules + echo 2. Choose "Always keep on this device" + echo 3. Then run: scripts\fix-onedrive-lock.ps1 + echo 4. Or run: scripts\fix-windows-build.bat + echo. + echo For detailed help, see: ONEDRIVE_FIX.md + echo. + exit /b 1 + ) + echo. +) else ( + echo [OK] Dependencies already installed + echo. +) + +REM Verify npx +where npx >nul 2>&1 +if errorlevel 1 ( + echo ERROR: npx not found + exit /b 1 +) + +REM Clear electron-builder cache to prevent code signing download +if exist "%LOCALAPPDATA%\electron-builder\Cache\winCodeSign" ( + echo Clearing winCodeSign cache (prevents symlink errors)... + rmdir /s /q "%LOCALAPPDATA%\electron-builder\Cache\winCodeSign" 2>nul + echo. +) + +REM Set ALL environment variables to disable code signing completely +set CSC_IDENTITY_AUTO_DISCOVERY=false +set WIN_CSC_LINK= +set CSC_LINK= + +REM Build (package.json already has sign: null) +echo ======================================== +echo Building Windows installer... +echo ======================================== +echo. +echo NOTE: Code signing is disabled (sign: null in package.json) +echo Environment variables set: CSC_IDENTITY_AUTO_DISCOVERY=false +echo. +echo If you get symbolic link errors: +echo 1. Enable Developer Mode: Win+I ^> Privacy ^& Security ^> For developers +echo 2. Or run PowerShell/CMD as Administrator +echo 3. Or run: scripts\clear-all-electron-cache.bat +echo. +call npx --yes electron-builder --win +if errorlevel 1 ( + echo. + echo ERROR: Build failed! + echo. + echo Common issues: + echo - Missing icon.ico: node scripts\generate-icons.js + echo - Check error messages above + echo. + exit /b 1 +) + +echo. +echo ======================================== +echo Build complete! +echo ======================================== +echo. +echo Output: %DESKTOP_DIR%\dist\ +echo. +dir /b "%DESKTOP_DIR%\dist\*.exe" 2>nul +echo. + +endlocal diff --git a/scripts/build-desktop-windows.bat b/scripts/build-desktop-windows.bat new file mode 100644 index 00000000..2f93f6d1 --- /dev/null +++ b/scripts/build-desktop-windows.bat @@ -0,0 +1,141 @@ +@echo off +REM Build script for TimeTracker Desktop App (Windows) +REM This is the native Windows batch script - use this instead of .sh on Windows + +setlocal enabledelayedexpansion + +set SCRIPT_DIR=%~dp0 +set PROJECT_ROOT=%SCRIPT_DIR%.. +set DESKTOP_DIR=%PROJECT_ROOT%\desktop + +cd /d "%PROJECT_ROOT%" + +REM Sync version from setup.py to package.json +echo Syncing version from setup.py... +python "%SCRIPT_DIR%sync-desktop-version.py" +if errorlevel 1 ( + echo ERROR: Failed to sync version + exit /b 1 +) +echo. + +cd /d "%DESKTOP_DIR%" + +echo Building TimeTracker Desktop App... +echo. + +REM Check Node.js +where node >nul 2>&1 +if errorlevel 1 ( + echo ERROR: Node.js is not installed or not in PATH + exit /b 1 +) + +echo Node.js version: +node --version +echo npm version: +npm --version +echo. + +REM Prepare assets +echo Preparing desktop assets... +call "%SCRIPT_DIR%prepare-desktop-assets.sh" +if errorlevel 1 ( + echo WARNING: Asset preparation had issues, continuing anyway... +) +echo. + +REM Check if node_modules exists and is valid +if exist "node_modules\electron\package.json" ( + if exist "node_modules\electron-builder\package.json" ( + node -e "require('electron')" >nul 2>&1 + if not errorlevel 1 ( + echo [OK] node_modules appears valid, skipping install + goto :skip_install + ) + ) +) + +echo Installing dependencies... +if not exist "node_modules" ( + echo Installing dependencies (first time)... + call npm install --prefer-offline --no-audit --loglevel=warn + if errorlevel 1 ( + echo. + echo ERROR: npm install failed + echo. + echo This is a common issue on Windows, especially with OneDrive: + echo - OneDrive file locking prevents npm from working properly + echo - Antivirus may be scanning files during install + echo - File permissions may be restricted + echo. + echo Solutions: + echo 1. Exclude node_modules from OneDrive sync: + echo - Right-click desktop\node_modules folder + echo - Choose "Always keep on this device" or exclude from sync + echo 2. Temporarily disable antivirus real-time scanning + echo 3. Run as Administrator + echo 4. Move project outside OneDrive folder + echo 5. Use WSL instead of Command Prompt + echo. + exit /b 1 + ) +) else ( + echo Updating dependencies... + call npm ci --prefer-offline --no-audit --loglevel=warn + if errorlevel 1 ( + echo npm ci failed, trying npm install... + call npm install --prefer-offline --no-audit --loglevel=warn + if errorlevel 1 ( + echo. + echo ERROR: npm install failed + echo. + echo Windows/OneDrive specific solutions: + echo 1. Close all programs that might be using node_modules + echo 2. Exclude desktop\node_modules from OneDrive sync + echo 3. Run PowerShell as Administrator and try again + echo 4. Delete node_modules and try: npm install + echo 5. Move project outside OneDrive + echo. + exit /b 1 + ) + ) +) +echo. + +:skip_install + +REM Verify electron-builder is available +where npx >nul 2>&1 +if errorlevel 1 ( + echo ERROR: npx is not available + exit /b 1 +) + +REM Set environment variable to disable code signing completely +set CSC_IDENTITY_AUTO_DISCOVERY=false + +REM Build for Windows (package.json already has sign: null) +echo Building Windows installer... +echo NOTE: Code signing is disabled (sign: null, CSC_IDENTITY_AUTO_DISCOVERY=false) +echo. +call npx --yes electron-builder --win +if errorlevel 1 ( + echo. + echo ERROR: Build failed + echo. + echo Check the error messages above for details. + echo Common issues: + echo - Missing icon.ico file (generate with: node scripts\generate-icons.js) + echo - Insufficient permissions + echo - Antivirus blocking the build + echo. + exit /b 1 +) + +echo. +echo Build complete! +echo Outputs: %DESKTOP_DIR%\dist\ +echo. + +endlocal diff --git a/scripts/build-desktop-windows.ps1 b/scripts/build-desktop-windows.ps1 new file mode 100644 index 00000000..dfe96030 --- /dev/null +++ b/scripts/build-desktop-windows.ps1 @@ -0,0 +1,134 @@ +# Build script for TimeTracker Desktop App (Windows PowerShell) +# This is the native Windows PowerShell script - use this instead of .sh on Windows + +$ErrorActionPreference = "Stop" + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$ProjectRoot = Split-Path -Parent $ScriptDir +$DesktopDir = Join-Path $ProjectRoot "desktop" + +Set-Location $ProjectRoot + +# Sync version from setup.py to package.json +Write-Host "Syncing version from setup.py..." -ForegroundColor Cyan +python "$ScriptDir\sync-desktop-version.py" +if ($LASTEXITCODE -ne 0) { + Write-Host "ERROR: Failed to sync version" -ForegroundColor Red + exit 1 +} +Write-Host "" + +Set-Location $DesktopDir + +Write-Host "Building TimeTracker Desktop App..." -ForegroundColor Cyan +Write-Host "" + +# Check Node.js +if (-not (Get-Command node -ErrorAction SilentlyContinue)) { + Write-Host "ERROR: Node.js is not installed or not in PATH" -ForegroundColor Red + exit 1 +} + +Write-Host "Node.js version: $(node --version)" +Write-Host "npm version: $(npm --version)" +Write-Host "" + +# Prepare assets +Write-Host "Preparing desktop assets..." -ForegroundColor Cyan +& bash "$ScriptDir\prepare-desktop-assets.sh" 2>&1 | Out-String +Write-Host "" + +# Check if node_modules exists and is valid +$NodeModulesValid = $false +if (Test-Path "node_modules") { + if ((Test-Path "node_modules\electron\package.json") -and + (Test-Path "node_modules\electron-builder\package.json")) { + try { + node -e "require('electron')" 2>$null | Out-Null + $NodeModulesValid = $true + Write-Host " [OK] node_modules appears valid, skipping install" -ForegroundColor Green + } catch { + $NodeModulesValid = $false + } + } +} + +if (-not $NodeModulesValid) { + Write-Host "Installing dependencies..." -ForegroundColor Cyan + + if (-not (Test-Path "node_modules")) { + Write-Host " Installing dependencies (first time)..." -ForegroundColor Yellow + npm install --prefer-offline --no-audit --loglevel=warn + if ($LASTEXITCODE -ne 0) { + Write-Host "" + Write-Host "ERROR: npm install failed" -ForegroundColor Red + Write-Host "" + Write-Host "This is a common issue on Windows, especially with OneDrive:" -ForegroundColor Yellow + Write-Host " - OneDrive file locking prevents npm from working properly" + Write-Host " - Antivirus may be scanning files during install" + Write-Host " - File permissions may be restricted" + Write-Host "" + Write-Host "Solutions:" -ForegroundColor Cyan + Write-Host " 1. Exclude node_modules from OneDrive sync" + Write-Host " 2. Temporarily disable antivirus real-time scanning" + Write-Host " 3. Run PowerShell as Administrator" + Write-Host " 4. Move project outside OneDrive folder" + Write-Host " 5. Use WSL instead of PowerShell" + Write-Host "" + exit 1 + } + } else { + Write-Host " Updating dependencies..." -ForegroundColor Yellow + npm ci --prefer-offline --no-audit --loglevel=warn + if ($LASTEXITCODE -ne 0) { + Write-Host " npm ci failed, trying npm install..." -ForegroundColor Yellow + npm install --prefer-offline --no-audit --loglevel=warn + if ($LASTEXITCODE -ne 0) { + Write-Host "" + Write-Host "ERROR: npm install failed" -ForegroundColor Red + Write-Host "" + Write-Host "Windows/OneDrive specific solutions:" -ForegroundColor Yellow + Write-Host " 1. Close all programs that might be using node_modules" + Write-Host " 2. Exclude desktop\node_modules from OneDrive sync" + Write-Host " 3. Run PowerShell as Administrator and try again" + Write-Host " 4. Delete node_modules and try: npm install" + Write-Host " 5. Move project outside OneDrive" + Write-Host "" + exit 1 + } + } + } + Write-Host "" +} + +# Verify electron-builder is available +if (-not (Get-Command npx -ErrorAction SilentlyContinue)) { + Write-Host "ERROR: npx is not available" -ForegroundColor Red + exit 1 +} + +# Set environment variable to disable code signing completely +$env:CSC_IDENTITY_AUTO_DISCOVERY = "false" + +# Build for Windows (package.json already has sign: null) +Write-Host "Building Windows installer..." -ForegroundColor Cyan +Write-Host "NOTE: Code signing is disabled (sign: null, CSC_IDENTITY_AUTO_DISCOVERY=false)" -ForegroundColor Yellow +Write-Host "" +npx --yes electron-builder --win +if ($LASTEXITCODE -ne 0) { + Write-Host "" + Write-Host "ERROR: Build failed" -ForegroundColor Red + Write-Host "" + Write-Host "Check the error messages above for details." -ForegroundColor Yellow + Write-Host "Common issues:" -ForegroundColor Yellow + Write-Host " - Missing icon.ico file (generate with: node scripts\generate-icons.js)" + Write-Host " - Insufficient permissions" + Write-Host " - Antivirus blocking the build" + Write-Host "" + exit 1 +} + +Write-Host "" +Write-Host "Build complete!" -ForegroundColor Green +Write-Host "Outputs: $DesktopDir\dist\" -ForegroundColor Cyan +Write-Host "" diff --git a/scripts/build-desktop.bat b/scripts/build-desktop.bat index 9c429fbe..ffbf3fed 100644 --- a/scripts/build-desktop.bat +++ b/scripts/build-desktop.bat @@ -1,11 +1,22 @@ @echo off REM Build script for TimeTracker Desktop App (Electron) - Windows -setlocal +setlocal enabledelayedexpansion set SCRIPT_DIR=%~dp0 set PROJECT_ROOT=%SCRIPT_DIR%.. set DESKTOP_DIR=%PROJECT_ROOT%\desktop +cd /d "%PROJECT_ROOT%" + +REM Sync version from setup.py to package.json +echo Syncing version from setup.py... +python "%SCRIPT_DIR%sync-desktop-version.py" +if errorlevel 1 ( + echo ERROR: Failed to sync version + exit /b 1 +) +echo. + cd /d "%DESKTOP_DIR%" echo Building TimeTracker Desktop App... @@ -24,37 +35,125 @@ echo npm version: npm --version echo. -REM Install dependencies -echo Installing dependencies... -if not exist "node_modules" ( - call npm install --prefer-offline --no-audit --loglevel=warn -) else ( - call npm ci --prefer-offline --no-audit --loglevel=warn - if errorlevel 1 ( - call npm install --prefer-offline --no-audit --loglevel=warn +echo Checking node_modules... +REM Check if node_modules exists and is valid +set SKIP_INSTALL=0 +if exist "node_modules\electron\package.json" ( + if exist "node_modules\electron-builder\package.json" ( + REM Try a quick test + node -e "require('electron')" >nul 2>&1 + if not errorlevel 1 ( + set SKIP_INSTALL=1 + echo [OK] node_modules appears valid, skipping install + echo. + ) ) ) + +REM Install dependencies +echo SKIP_INSTALL = %SKIP_INSTALL% +if "%SKIP_INSTALL%"=="0" ( + echo Installing dependencies... + if not exist "node_modules" ( + echo Installing dependencies (first time)... + call npm install --prefer-offline --no-audit --loglevel=warn + if errorlevel 1 ( + echo. + echo ERROR: npm install failed + echo. + echo This is a common issue on Windows, especially with OneDrive: + echo - OneDrive file locking prevents npm from working properly + echo - Antivirus may be scanning files during install + echo - File permissions may be restricted + echo. + echo Solutions: + echo 1. Exclude node_modules from OneDrive sync (MOST IMPORTANT!) + echo - Right-click desktop\node_modules folder + echo - Choose "Always keep on this device" + echo 2. Run: scripts\fix-windows-build.bat + echo 3. Run this script as Administrator + echo 4. Move project outside OneDrive folder + echo 5. Use WSL instead of Command Prompt + echo. + exit /b 1 + ) + ) else ( + echo Updating dependencies... + call npm ci --prefer-offline --no-audit --loglevel=warn + if errorlevel 1 ( + echo npm ci failed, trying npm install... + call npm install --prefer-offline --no-audit --loglevel=warn + if errorlevel 1 ( + echo. + echo ERROR: npm install failed + echo. + echo Windows/OneDrive specific solutions: + echo 1. Close all programs that might be using node_modules + echo 2. Exclude desktop\node_modules from OneDrive sync + echo 3. Run Command Prompt as Administrator and try again + echo 4. Delete node_modules and try: npm install + echo 5. Move project outside OneDrive + echo. + exit /b 1 + ) + ) + ) + echo. +) + +REM Verify electron-builder is available +echo Checking npx... +where npx >nul 2>&1 if errorlevel 1 ( - echo ERROR: Failed to install dependencies + echo ERROR: npx is not available + echo Make sure Node.js is installed and npm is in PATH exit /b 1 ) +echo [OK] npx found echo. +REM Set environment variable to disable code signing completely +set CSC_IDENTITY_AUTO_DISCOVERY=false + +REM Clear electron-builder cache if symlink errors occurred +if exist "%LOCALAPPDATA%\electron-builder\Cache\winCodeSign" ( + echo Clearing winCodeSign cache (prevents symlink errors)... + rmdir /s /q "%LOCALAPPDATA%\electron-builder\Cache\winCodeSign" 2>nul + echo. +) + REM Build +echo ======================================== +echo Starting build process... +echo ======================================== +echo. +echo NOTE: Code signing is disabled (sign: null, CSC_IDENTITY_AUTO_DISCOVERY=false) +echo If you still get symbolic link errors: +echo 1. Enable Developer Mode: Win+I ^> Privacy ^& Security ^> For developers +echo 2. Or run PowerShell/CMD as Administrator +echo 3. Or run: scripts\clear-all-electron-cache.bat +echo. set PLATFORM=%1 if "%PLATFORM%"=="" set PLATFORM=win if /i "%PLATFORM%"=="win" ( echo Building Windows installer... - call npm run build:win + call npx --yes electron-builder --win if errorlevel 1 ( echo ERROR: Windows build failed + echo. + echo Common issues: + echo - Missing icon.ico file (generate with: node scripts\generate-icons.js) + echo - Insufficient permissions + echo - Antivirus blocking the build + echo - Symbolic link errors (enable Developer Mode or run as Admin) + echo. exit /b 1 ) ) else if /i "%PLATFORM%"=="all" ( echo Building for all supported platforms... echo Note: Will automatically build for platforms supported on your OS - call npm run build:all + call npx --yes electron-builder --win --mac --linux if errorlevel 1 ( echo ERROR: Build failed exit /b 1 diff --git a/scripts/build-desktop.sh b/scripts/build-desktop.sh index 0ebe63a2..0751fe61 100644 --- a/scripts/build-desktop.sh +++ b/scripts/build-desktop.sh @@ -1,17 +1,56 @@ #!/bin/bash # Build script for TimeTracker Desktop App (Electron) +# For Windows, use build-desktop-windows.bat or build-desktop-windows.ps1 instead set -e +# Detect if running on Windows and suggest using native scripts +if [[ "$(uname -s)" == MINGW* ]] || [[ "$(uname -s)" == MSYS* ]] || [[ "$OSTYPE" == "msys" ]]; then + echo "NOTE: You're on Windows. For better compatibility, consider using:" + echo " - scripts\build-desktop-windows.bat (Command Prompt)" + echo " - scripts\build-desktop-windows.ps1 (PowerShell)" + echo "" + read -p "Continue with bash script anyway? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Build cancelled. Use the Windows-native scripts instead." + exit 0 + fi + echo "" +fi + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" DESKTOP_DIR="$PROJECT_ROOT/desktop" +cd "$PROJECT_ROOT" + +# Sync version from setup.py to package.json +echo "Syncing version from setup.py..." +python3 "$SCRIPT_DIR/sync-desktop-version.py" +if [ $? -ne 0 ]; then + echo "ERROR: Failed to sync version" + exit 1 +fi +echo "" + cd "$DESKTOP_DIR" echo "Building TimeTracker Desktop App..." echo "" +# Prepare assets (logo, icons) - only once +# Use a temp file to track if already prepared (works across subshells) +ASSETS_CHECK_FILE="$DESKTOP_DIR/.assets_prepared" +if [ ! -f "$ASSETS_CHECK_FILE" ]; then + echo "Preparing desktop assets..." + bash "$SCRIPT_DIR/prepare-desktop-assets.sh" || { + echo "WARNING: Asset preparation had issues, continuing anyway..." + } + touch "$ASSETS_CHECK_FILE" + echo "" +fi + # Check Node.js if ! command -v node &> /dev/null; then echo "ERROR: Node.js is not installed or not in PATH" @@ -22,60 +61,379 @@ echo "Node.js version: $(node --version)" echo "npm version: $(npm --version)" echo "" +# Check for logo file (required for splash screen and branding) +if [ ! -f "assets/logo.svg" ]; then + echo "WARNING: assets/logo.svg not found" + echo " Creating from main logo..." + if [ -f "$PROJECT_ROOT/app/static/images/timetracker-logo.svg" ]; then + mkdir -p assets + cp "$PROJECT_ROOT/app/static/images/timetracker-logo.svg" assets/logo.svg + echo " ✓ Logo copied to assets/logo.svg" + else + echo " ERROR: Main logo not found at app/static/images/timetracker-logo.svg" + echo " Please ensure the logo file exists" + exit 1 + fi +fi + +# Check for icon files (warn if missing, but don't fail) +# Only check if not already prepared +if [ ! -f "$ASSETS_CHECK_FILE" ]; then + echo "Checking icon files..." + MISSING_ICONS=0 + if [ ! -f "assets/icon.ico" ]; then + echo " WARNING: assets/icon.ico not found (required for Windows builds)" + MISSING_ICONS=1 + fi + if [ ! -f "assets/icon.icns" ]; then + echo " WARNING: assets/icon.icns not found (required for macOS builds)" + MISSING_ICONS=1 + fi + if [ ! -f "assets/icon.png" ]; then + echo " WARNING: assets/icon.png not found (required for Linux builds)" + MISSING_ICONS=1 + fi + + if [ $MISSING_ICONS -eq 1 ]; then + echo "" + echo "NOTE: Some icon files are missing. The build will continue, but:" + echo " - Windows builds require icon.ico" + echo " - macOS builds require icon.icns" + echo " - Linux builds require icon.png" + echo "" + echo "To generate icons, run:" + echo " node scripts/generate-icons.js" + echo " Then convert the generated PNG files to .ico/.icns as needed" + echo "" + # On Windows/Git Bash, skip interactive prompt if CI or non-interactive + if [ -t 0 ] && [ -z "$CI" ]; then + read -p "Continue anyway? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Build cancelled" + exit 1 + fi + else + echo "Continuing (non-interactive mode)..." + fi + echo "" + fi +fi + # Install dependencies echo "Installing dependencies..." -if [ ! -d "node_modules" ]; then - npm install --prefer-offline --no-audit --loglevel=warn -else - npm ci --prefer-offline --no-audit --loglevel=warn || npm install --prefer-offline --no-audit --loglevel=warn + +# Check if node_modules exists and is valid +NODE_MODULES_VALID=false +if [ -d "node_modules" ]; then + # Check if electron-builder is available (either in node_modules/.bin or via npx) + if [ -f "node_modules/.bin/electron-builder" ] || [ -f "node_modules/electron-builder/out/builder.js" ]; then + # Try a quick test to see if electron is accessible + if node -e "require('electron')" 2>/dev/null; then + NODE_MODULES_VALID=true + echo " ✓ node_modules appears valid, skipping install" + fi + fi +fi + + if [ "$NODE_MODULES_VALID" = false ] || [ ! -f "node_modules/.bin/electron-builder" ]; then + # Detect Windows/OneDrive issues + IS_WINDOWS=false + IS_ONEDRIVE=false + if [[ "$(uname -s)" == MINGW* ]] || [[ "$(uname -s)" == MSYS* ]] || [[ "$(uname -s)" == CYGWIN* ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then + IS_WINDOWS=true + fi + if [[ "$PROJECT_ROOT" == *"OneDrive"* ]]; then + IS_ONEDRIVE=true + fi + + if [ ! -d "node_modules" ]; then + echo " Installing dependencies (first time)..." + if ! npm install --prefer-offline --no-audit --loglevel=warn; then + echo "" + echo "ERROR: npm install failed" + if [ "$IS_WINDOWS" = true ] || [ "$IS_ONEDRIVE" = true ]; then + echo "" + echo "This is a common issue on Windows, especially with OneDrive:" + echo " - OneDrive file locking prevents npm from working properly" + echo " - Antivirus may be scanning files during install" + echo " - File permissions may be restricted" + echo "" + echo "Solutions:" + echo " 1. Exclude node_modules from OneDrive sync:" + echo " - Right-click desktop/node_modules folder" + echo " - Choose 'Always keep on this device' or exclude from sync" + echo " 2. Temporarily disable antivirus real-time scanning" + echo " 3. Run as Administrator" + echo " 4. Move project outside OneDrive folder" + echo " 5. Use WSL instead of Git Bash" + echo "" + else + echo "" + echo "This is common on Linux/Mac, especially when:" + echo " - Files are locked by another process" + echo " - Insufficient permissions (try with sudo)" + echo " - On WSL: Project is in OneDrive (file locking)" + echo "" + echo "Solutions:" + echo " 1. Run: scripts/fix-desktop-build.sh" + echo " 2. Check file permissions: ls -la" + echo " 3. If on WSL with OneDrive: Exclude node_modules from sync" + echo " 4. Try: sudo npm install (if permission issue)" + echo "" + fi + exit 1 + fi + else + echo " Updating dependencies..." + # Try npm ci first (faster, more reliable) + if ! npm ci --prefer-offline --no-audit --loglevel=warn 2>&1; then + echo " npm ci failed, trying npm install..." + # Clean npm cache and retry + if [ "$IS_WINDOWS" = true ] || [ "$IS_ONEDRIVE" = true ]; then + echo " Attempting to fix Windows/OneDrive issues..." + # Try with different flags for Windows + if ! npm install --prefer-offline --no-audit --loglevel=warn --no-optional 2>&1; then + echo "" + echo "ERROR: npm install failed" + echo "" + echo "Windows/OneDrive specific solutions:" + echo " 1. Close all programs that might be using node_modules" + echo " 2. Exclude desktop/node_modules from OneDrive sync" + echo " 3. Run PowerShell as Administrator and try again" + echo " 4. Delete node_modules and try: npm install" + echo " 5. Move project outside OneDrive" + echo "" + echo "To delete node_modules and retry:" + echo " cd desktop" + echo " rm -rf node_modules" + echo " npm install" + echo "" + exit 1 + fi + else + if ! npm install --prefer-offline --no-audit --loglevel=warn; then + echo "" + echo "ERROR: npm install failed" + echo "" + echo "Solutions:" + echo " 1. Run: scripts/fix-desktop-build.sh" + echo " 2. Check file permissions: ls -la" + echo " 3. If on WSL with OneDrive: Exclude node_modules from sync" + echo " 4. Try: sudo npm install (if permission issue)" + echo "" + exit 1 + fi + fi + fi + fi fi echo "" # Build PLATFORM="${1:-current}" +# Detect platform more accurately (handle Git Bash on Windows) +detect_platform() { + local os=$(uname -s) + # Git Bash on Windows reports as MINGW64_NT or MINGW32_NT + if [[ "$os" == MINGW* ]] || [[ "$os" == MSYS* ]] || [[ "$os" == CYGWIN* ]]; then + echo "win32" + elif [[ "$os" == Linux* ]]; then + echo "linux" + elif [[ "$os" == Darwin* ]]; then + echo "darwin" + else + echo "unknown" + fi +} + +CURRENT_PLATFORM=$(detect_platform) + case "$PLATFORM" in win|windows) + if [ "$CURRENT_PLATFORM" != "win32" ] && [ "$CURRENT_PLATFORM" != "unknown" ]; then + echo "WARNING: Building Windows installer on non-Windows platform" + echo " This may not work correctly. Use CI/CD for cross-platform builds." + fi + + # Clear electron-builder cache to prevent code signing download (Windows only) + if [ "$CURRENT_PLATFORM" = "win32" ]; then + # Try both Windows path formats (Git Bash may use different env vars) + CACHE_BASE="" + if [ -n "$LOCALAPPDATA" ]; then + CACHE_BASE="$LOCALAPPDATA/electron-builder" + elif [ -n "$HOME" ]; then + CACHE_BASE="$HOME/AppData/Local/electron-builder" + fi + + if [ -n "$CACHE_BASE" ] && [ -d "$CACHE_BASE/Cache/winCodeSign" ]; then + echo "Clearing winCodeSign cache (prevents symlink errors)..." + rm -rf "$CACHE_BASE/Cache/winCodeSign" 2>/dev/null || true + echo "" + fi + fi + echo "Building Windows installer..." - npm run build:win + echo "NOTE: Code signing is disabled (sign: null)" + echo "" + + # Set environment variable to prevent code signing + export CSC_IDENTITY_AUTO_DISCOVERY=false + + echo " If you get symbolic link errors:" + echo " 1. Enable Developer Mode: Win+I > Privacy & Security > For developers" + echo " 2. Or run PowerShell/CMD as Administrator" + echo " 3. Or run: ./scripts/clear-all-electron-cache.sh" + echo "" + CSC_IDENTITY_AUTO_DISCOVERY=false npx --yes electron-builder --win + if [ $? -ne 0 ]; then + echo "" + echo "ERROR: Windows build failed" + echo "" + echo "Common issues:" + echo " - Missing icon.ico file (generate with: node scripts/generate-icons.js)" + echo " - Symbolic link errors (enable Developer Mode or run as Administrator)" + echo " - Insufficient permissions" + echo " - Antivirus blocking the build" + echo "" + exit 1 + fi ;; mac|macos) - if [ "$(uname)" != "Darwin" ]; then + if [ "$CURRENT_PLATFORM" != "darwin" ]; then echo "ERROR: macOS builds can only be done on macOS" exit 1 fi echo "Building macOS DMG..." - npm run build:mac + npx --yes electron-builder --mac + if [ $? -ne 0 ]; then + echo "ERROR: macOS build failed" + exit 1 + fi ;; linux) - if [ "$(uname)" = "Darwin" ] || [[ "$(uname)" == MINGW* ]]; then + if [ "$CURRENT_PLATFORM" != "linux" ]; then echo "ERROR: Linux builds can only be done on Linux" exit 1 fi echo "Building Linux packages..." - npm run build:linux + npx --yes electron-builder --linux + if [ $? -ne 0 ]; then + echo "ERROR: Linux build failed" + exit 1 + fi ;; all|all-platforms) echo "Building for all supported platforms..." echo "Note: Will automatically build for platforms supported on your OS" - npm run build:all + npx --yes electron-builder --win --mac --linux + if [ $? -ne 0 ]; then + echo "ERROR: Build failed" + exit 1 + fi ;; current) # Build for current platform - OS=$(uname -s) - case "$OS" in - Linux*) - echo "Building Linux packages..." - npm run build:linux + case "$CURRENT_PLATFORM" in + win32) + echo "Detected Windows (Git Bash/MSYS)" + + # Clear electron-builder cache to prevent code signing download + CACHE_BASE="" + if [ -n "$LOCALAPPDATA" ]; then + CACHE_BASE="$LOCALAPPDATA/electron-builder" + elif [ -n "$HOME" ]; then + CACHE_BASE="$HOME/AppData/Local/electron-builder" + fi + + if [ -n "$CACHE_BASE" ] && [ -d "$CACHE_BASE/Cache/winCodeSign" ]; then + echo "Clearing winCodeSign cache (prevents symlink errors)..." + rm -rf "$CACHE_BASE/Cache/winCodeSign" 2>/dev/null || true + echo "" + fi + + echo "Building Windows installer..." + echo "NOTE: Code signing is disabled (sign: null)" + echo "" + + # Set ALL environment variables to prevent code signing completely + export CSC_IDENTITY_AUTO_DISCOVERY=false + export WIN_CSC_LINK="" + export WIN_CSC_KEY_PASSWORD="" + export CSC_LINK="" + export CSC_KEY_PASSWORD="" + + echo " If you STILL get symbolic link errors:" + echo " 1. Enable Developer Mode: Win+I > Privacy & Security > For developers" + echo " 2. Or run PowerShell/CMD as Administrator" + echo " 3. Or run: ./scripts/clear-all-electron-cache.sh" + echo " 4. Or try: ./scripts/build-desktop-no-sign.sh (more aggressive)" + echo "" + + # Build with multiple env vars and explicit CLI flags to ensure no code signing + CSC_IDENTITY_AUTO_DISCOVERY=false \ + WIN_CSC_LINK="" \ + CSC_LINK="" \ + npx --yes electron-builder --win --config.win.sign=null --config.win.signingHashAlgorithms=null --config.win.signDlls=false 2>&1 | tee /tmp/electron-builder.log || { + echo "" + echo "Build failed. Checking if it's the symlink issue..." + if grep -q "symbolic link" /tmp/electron-builder.log 2>/dev/null || grep -q "winCodeSign" /tmp/electron-builder.log 2>/dev/null; then + echo "" + echo "================================================================================" + echo "SYMLINK ERROR DETECTED!" + echo "================================================================================" + echo "" + echo "electron-builder is still trying to download code signing tools." + echo "" + echo "SOLUTION: Use the no-sign build script instead:" + echo " ./scripts/build-desktop-no-sign.sh" + echo "" + echo "Or manually enable Developer Mode in Windows:" + echo " Win+I > Privacy & Security > For developers > Developer Mode" + echo "" + echo "================================================================================" + fi + exit 1 + } + if [ $? -ne 0 ]; then + echo "" + echo "ERROR: Windows build failed" + echo "" + echo "Common issues:" + echo " - Missing icon.ico file (generate with: node scripts/generate-icons.js)" + echo " - Symbolic link errors (enable Developer Mode or run as Administrator)" + echo " - Insufficient permissions" + echo "" + exit 1 + fi ;; - Darwin*) + linux) + echo "Detected Linux" + echo "Building Linux packages..." + npx --yes electron-builder --linux + if [ $? -ne 0 ]; then + echo "ERROR: Linux build failed" + exit 1 + fi + ;; + darwin) + echo "Detected macOS" echo "Building macOS DMG..." - npm run build:mac + npx --yes electron-builder --mac + if [ $? -ne 0 ]; then + echo "ERROR: macOS build failed" + exit 1 + fi ;; *) - echo "Unknown platform: $OS" - echo "Building for all platforms..." - npm run build + echo "Unknown platform: $(uname -s)" + echo "Attempting to build for Windows (default)..." + npx --yes electron-builder --win + if [ $? -ne 0 ]; then + echo "ERROR: Build failed" + exit 1 + fi ;; esac ;; @@ -85,6 +443,19 @@ case "$PLATFORM" in ;; esac +# Clean up assets check file +rm -f "$ASSETS_CHECK_FILE" 2>/dev/null || true + echo "" +echo "========================================" echo "Build complete!" +echo "========================================" +echo "" echo "Outputs: $DESKTOP_DIR/dist/" +if [ "$CURRENT_PLATFORM" = "win32" ]; then + echo "" + if [ -d "$DESKTOP_DIR/dist" ]; then + echo "Built files:" + ls -lh "$DESKTOP_DIR/dist"/*.exe 2>/dev/null || ls -lh "$DESKTOP_DIR/dist"/*.nsis 2>/dev/null || echo " (Check dist/ directory for build outputs)" + fi +fi diff --git a/scripts/build-mobile.bat b/scripts/build-mobile.bat index 7ed19bd7..5f4e97c9 100644 --- a/scripts/build-mobile.bat +++ b/scripts/build-mobile.bat @@ -6,6 +6,17 @@ set SCRIPT_DIR=%~dp0 set PROJECT_ROOT=%SCRIPT_DIR%.. set MOBILE_DIR=%PROJECT_ROOT%\mobile +cd /d "%PROJECT_ROOT%" + +REM Sync version from setup.py to mobile app +echo Syncing version from setup.py... +python "%SCRIPT_DIR%sync-mobile-version.py" +if errorlevel 1 ( + echo ERROR: Failed to sync version + exit /b 1 +) +echo. + cd /d "%MOBILE_DIR%" echo Building TimeTracker Mobile App... diff --git a/scripts/build-mobile.sh b/scripts/build-mobile.sh index d1310e02..be6a7cf7 100644 --- a/scripts/build-mobile.sh +++ b/scripts/build-mobile.sh @@ -7,6 +7,17 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" MOBILE_DIR="$PROJECT_ROOT/mobile" +cd "$PROJECT_ROOT" + +# Sync version from setup.py to mobile app +echo "Syncing version from setup.py..." +python3 "$SCRIPT_DIR/sync-mobile-version.py" +if [ $? -ne 0 ]; then + echo "ERROR: Failed to sync version" + exit 1 +fi +echo "" + cd "$MOBILE_DIR" echo "Building TimeTracker Mobile App..." diff --git a/scripts/fix-onedrive-lock.ps1 b/scripts/fix-onedrive-lock.ps1 new file mode 100644 index 00000000..a7441fd9 --- /dev/null +++ b/scripts/fix-onedrive-lock.ps1 @@ -0,0 +1,136 @@ +# Fix OneDrive file locking issues for npm install +# This PowerShell script handles locked files better than batch files + +$ErrorActionPreference = "Continue" + +$ProjectRoot = Split-Path -Parent $PSScriptRoot +$DesktopDir = Join-Path $ProjectRoot "desktop" +$NodeModulesDir = Join-Path $DesktopDir "node_modules" + +Write-Host "Fixing OneDrive file locking issues..." -ForegroundColor Cyan +Write-Host "" + +# Check if in OneDrive +if ($ProjectRoot -like "*OneDrive*") { + Write-Host "WARNING: Project is in OneDrive!" -ForegroundColor Yellow + Write-Host "OneDrive file locking causes npm install to fail." -ForegroundColor Yellow + Write-Host "" + Write-Host "RECOMMENDED: Exclude node_modules from OneDrive sync:" -ForegroundColor Cyan + Write-Host " 1. Right-click: $DesktopDir\node_modules" -ForegroundColor White + Write-Host " 2. Choose 'Always keep on this device'" -ForegroundColor White + Write-Host " 3. Or exclude it from OneDrive sync entirely" -ForegroundColor White + Write-Host "" + $continue = Read-Host "Continue anyway? (y/N)" + if ($continue -ne "y" -and $continue -ne "Y") { + exit 0 + } + Write-Host "" +} + +Set-Location $DesktopDir + +# Clean npm cache +Write-Host "Cleaning npm cache..." -ForegroundColor Cyan +try { + npm cache clean --force 2>&1 | Out-Null + Write-Host " [OK] Cache cleaned" -ForegroundColor Green +} catch { + Write-Host " [WARNING] Cache clean had issues, continuing..." -ForegroundColor Yellow +} +Write-Host "" + +# Remove node_modules with retries and better error handling +if (Test-Path $NodeModulesDir) { + Write-Host "Removing node_modules..." -ForegroundColor Cyan + Write-Host " (This may take a moment, especially with OneDrive locking)" -ForegroundColor Yellow + Write-Host "" + + # Method 1: Try Remove-Item with -Recurse -Force + $maxRetries = 5 + $retry = 0 + $removed = $false + + while ($retry -lt $maxRetries -and -not $removed) { + $retry++ + Write-Host " Attempt $retry/$maxRetries..." -ForegroundColor Yellow + + try { + # Try to remove locked files first + Get-ChildItem -Path $NodeModulesDir -Recurse -Force -ErrorAction SilentlyContinue | + ForEach-Object { + try { + Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue + } catch { + # Ignore individual file errors + } + } + + # Remove directory + Remove-Item -Path $NodeModulesDir -Recurse -Force -ErrorAction Stop + $removed = $true + Write-Host " [OK] node_modules removed" -ForegroundColor Green + } catch { + if ($retry -lt $maxRetries) { + Write-Host " [WARNING] Attempt $retry failed, waiting 3 seconds..." -ForegroundColor Yellow + Start-Sleep -Seconds 3 + } else { + Write-Host " [ERROR] Failed to remove after $maxRetries attempts" -ForegroundColor Red + Write-Host "" + Write-Host "Manual steps required:" -ForegroundColor Yellow + Write-Host " 1. Close ALL programs (VS Code, terminals, File Explorer, etc.)" + Write-Host " 2. Open PowerShell as Administrator" + Write-Host " 3. Run: Remove-Item -Path '$NodeModulesDir' -Recurse -Force" + Write-Host " 4. Or manually delete the folder in File Explorer" + Write-Host "" + Write-Host "Or exclude from OneDrive sync first (recommended)" + exit 1 + } + } + } + Write-Host "" +} + +# Remove package-lock.json +if (Test-Path "package-lock.json") { + Write-Host "Removing package-lock.json..." -ForegroundColor Cyan + try { + Remove-Item -Path "package-lock.json" -Force + Write-Host " [OK] Removed" -ForegroundColor Green + } catch { + Write-Host " [WARNING] Could not remove package-lock.json" -ForegroundColor Yellow + } + Write-Host "" +} + +# Reinstall dependencies +Write-Host "Reinstalling dependencies..." -ForegroundColor Cyan +Write-Host " (This may take several minutes)" -ForegroundColor Yellow +Write-Host "" + +try { + npm install --prefer-offline --no-audit --loglevel=warn + if ($LASTEXITCODE -eq 0) { + Write-Host "" + Write-Host "[OK] Dependencies installed successfully!" -ForegroundColor Green + Write-Host "" + Write-Host "Next steps:" -ForegroundColor Cyan + Write-Host " 1. IMPORTANT: Exclude node_modules from OneDrive sync" -ForegroundColor Yellow + Write-Host " - Right-click: $DesktopDir\node_modules" -ForegroundColor White + Write-Host " - Choose 'Always keep on this device'" -ForegroundColor White + Write-Host " 2. Run the build: scripts\build-desktop-simple.bat" -ForegroundColor White + } else { + Write-Host "" + Write-Host "[ERROR] Installation failed" -ForegroundColor Red + Write-Host "" + Write-Host "Try these solutions:" -ForegroundColor Yellow + Write-Host " 1. EXCLUDE node_modules from OneDrive sync (MOST IMPORTANT!)" + Write-Host " 2. Run PowerShell as Administrator" + Write-Host " 3. Temporarily disable antivirus real-time scanning" + Write-Host " 4. Move project outside OneDrive folder" + exit 1 + } +} catch { + Write-Host "" + Write-Host "[ERROR] Installation failed: $_" -ForegroundColor Red + exit 1 +} diff --git a/scripts/generate-icons.js b/scripts/generate-icons.js new file mode 100644 index 00000000..c0fb8561 --- /dev/null +++ b/scripts/generate-icons.js @@ -0,0 +1,128 @@ +#!/usr/bin/env node + +/** + * Icon Generation Script for TimeTracker + * + * This script generates all required icon formats from the SVG logo. + * Requires: sharp (npm install sharp) + * + * Usage: node scripts/generate-icons.js + */ + +const fs = require('fs'); +const path = require('path'); + +// Check if sharp is available +let sharp; +try { + sharp = require('sharp'); +} catch (e) { + console.error('Error: sharp package is required. Install it with: npm install sharp'); + process.exit(1); +} + +const logoPath = path.join(__dirname, '../app/static/images/timetracker-logo.svg'); +const outputDir = path.join(__dirname, '../app/static/images'); +const desktopAssetsDir = path.join(__dirname, '../desktop/assets'); + +// Ensure output directories exist +[outputDir, desktopAssetsDir].forEach(dir => { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +}); + +async function generateIcons() { + console.log('Generating icons from SVG logo...\n'); + + if (!fs.existsSync(logoPath)) { + console.error(`Error: Logo file not found at ${logoPath}`); + process.exit(1); + } + + try { + // Read SVG + const svgBuffer = fs.readFileSync(logoPath); + + // Generate favicon.ico (multi-size) + console.log('Generating favicon.ico...'); + const faviconSizes = [16, 32, 48]; + const faviconImages = await Promise.all( + faviconSizes.map(size => + sharp(svgBuffer) + .resize(size, size) + .png() + .toBuffer() + ) + ); + // Note: Creating a proper .ico file requires additional library + // For now, we'll create a 32x32 PNG as favicon + await sharp(svgBuffer) + .resize(32, 32) + .png() + .toFile(path.join(outputDir, 'favicon-32x32.png')); + console.log(' ✓ Created favicon-32x32.png'); + + // Generate Apple Touch Icon (180x180) + console.log('Generating Apple Touch Icon...'); + await sharp(svgBuffer) + .resize(180, 180) + .png() + .toFile(path.join(outputDir, 'apple-touch-icon.png')); + console.log(' ✓ Created apple-touch-icon.png (180x180)'); + + // Generate Android Chrome Icons + console.log('Generating Android Chrome Icons...'); + await sharp(svgBuffer) + .resize(192, 192) + .png() + .toFile(path.join(outputDir, 'android-chrome-192x192.png')); + console.log(' ✓ Created android-chrome-192x192.png'); + + await sharp(svgBuffer) + .resize(512, 512) + .png() + .toFile(path.join(outputDir, 'android-chrome-512x512.png')); + console.log(' ✓ Created android-chrome-512x512.png'); + + // Generate Desktop Icons + console.log('\nGenerating Desktop Icons...'); + + // Linux PNG (512x512) + await sharp(svgBuffer) + .resize(512, 512) + .png() + .toFile(path.join(desktopAssetsDir, 'icon.png')); + console.log(' ✓ Created desktop/assets/icon.png (512x512)'); + + // Windows ICO (requires multi-size, creating PNG for now) + // Note: Proper .ico generation requires additional tools + await sharp(svgBuffer) + .resize(256, 256) + .png() + .toFile(path.join(desktopAssetsDir, 'icon-256x256.png')); + console.log(' ✓ Created desktop/assets/icon-256x256.png'); + console.log(' ⚠ Note: Convert to .ico using online tool or ImageMagick'); + + // macOS ICNS (requires iconutil, creating PNG for now) + await sharp(svgBuffer) + .resize(512, 512) + .png() + .toFile(path.join(desktopAssetsDir, 'icon-512x512.png')); + console.log(' ✓ Created desktop/assets/icon-512x512.png'); + console.log(' ⚠ Note: Convert to .icns using iconutil on macOS'); + + console.log('\n✓ Icon generation complete!'); + console.log('\nNext steps:'); + console.log('1. Convert icon-256x256.png to icon.ico for Windows'); + console.log('2. Convert icon-512x512.png to icon.icns for macOS'); + console.log('3. Use online tools like cloudconvert.com or iconverticons.com'); + + } catch (error) { + console.error('Error generating icons:', error); + process.exit(1); + } +} + +// Run the script +generateIcons(); diff --git a/scripts/sync-desktop-version.py b/scripts/sync-desktop-version.py new file mode 100644 index 00000000..8bd994db --- /dev/null +++ b/scripts/sync-desktop-version.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +Sync version from setup.py to desktop/package.json +This ensures the Electron app uses the same version as the main application. +""" + +import os +import sys +import json +import re + +def get_version_from_setup(): + """Read version from setup.py""" + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + setup_path = os.path.join(project_root, 'setup.py') + + if not os.path.exists(setup_path): + print(f"ERROR: setup.py not found at {setup_path}") + sys.exit(1) + + try: + with open(setup_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Extract version using regex + # Matches: version='X.Y.Z' or version="X.Y.Z" + version_match = re.search(r'version\s*=\s*[\'"]([^\'"]+)[\'"]', content) + + if version_match: + return version_match.group(1) + else: + print("ERROR: Could not find version in setup.py") + sys.exit(1) + except Exception as e: + print(f"ERROR: Failed to read setup.py: {e}") + sys.exit(1) + +def update_package_json(version): + """Update version in desktop/package.json""" + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + package_json_path = os.path.join(project_root, 'desktop', 'package.json') + + if not os.path.exists(package_json_path): + print(f"ERROR: package.json not found at {package_json_path}") + sys.exit(1) + + try: + # Read current package.json + with open(package_json_path, 'r', encoding='utf-8') as f: + package_data = json.load(f) + + old_version = package_data.get('version', 'unknown') + + # Update version + package_data['version'] = version + + # Write back to file with proper formatting + with open(package_json_path, 'w', encoding='utf-8') as f: + json.dump(package_data, f, indent=2, ensure_ascii=False) + f.write('\n') # Add trailing newline + + print(f"Updated desktop/package.json version: {old_version} -> {version}") + return True + except Exception as e: + print(f"ERROR: Failed to update package.json: {e}") + sys.exit(1) + +def main(): + """Main function""" + print("Syncing version from setup.py to desktop/package.json...") + + # Get version from setup.py + version = get_version_from_setup() + print(f"Found version in setup.py: {version}") + + # Update package.json + update_package_json(version) + + print("Version sync complete!") + +if __name__ == '__main__': + main() diff --git a/scripts/sync-mobile-version.py b/scripts/sync-mobile-version.py new file mode 100644 index 00000000..14f3e0b3 --- /dev/null +++ b/scripts/sync-mobile-version.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +""" +Sync version from setup.py to mobile app (Flutter pubspec.yaml and Android build.gradle) +This ensures the mobile app uses the same version as the main application. +""" + +import os +import sys +import re + +def get_version_from_setup(): + """Read version from setup.py""" + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + setup_path = os.path.join(project_root, 'setup.py') + + if not os.path.exists(setup_path): + print(f"ERROR: setup.py not found at {setup_path}") + sys.exit(1) + + try: + with open(setup_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Extract version using regex + # Matches: version='X.Y.Z' or version="X.Y.Z" + version_match = re.search(r'version\s*=\s*[\'"]([^\'"]+)[\'"]', content) + + if version_match: + return version_match.group(1) + else: + print("ERROR: Could not find version in setup.py") + sys.exit(1) + except Exception as e: + print(f"ERROR: Failed to read setup.py: {e}") + sys.exit(1) + +def update_pubspec_yaml(version): + """Update version in mobile/pubspec.yaml""" + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + pubspec_path = os.path.join(project_root, 'mobile', 'pubspec.yaml') + + if not os.path.exists(pubspec_path): + print(f"ERROR: pubspec.yaml not found at {pubspec_path}") + sys.exit(1) + + try: + # Read current pubspec.yaml + with open(pubspec_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Extract current version + version_match = re.search(r'^version:\s*([^\s+]+)', content, re.MULTILINE) + old_version = version_match.group(1) if version_match else 'unknown' + + # Parse version to get build number (if present) + # Format: X.Y.Z+build or X.Y.Z + version_parts = old_version.split('+') + build_number = version_parts[1] if len(version_parts) > 1 else '1' + + # Update version line + new_version_line = f"version: {version}+{build_number}" + content = re.sub( + r'^version:\s*[^\s+]+(?:\+\d+)?', + new_version_line, + content, + flags=re.MULTILINE + ) + + # Write back to file + with open(pubspec_path, 'w', encoding='utf-8') as f: + f.write(content) + + print(f"Updated mobile/pubspec.yaml version: {old_version} -> {new_version_line}") + return True + except Exception as e: + print(f"ERROR: Failed to update pubspec.yaml: {e}") + sys.exit(1) + +def update_android_build_gradle(version): + """Update version in mobile/android/local.properties (Flutter will use this)""" + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + local_properties_path = os.path.join(project_root, 'mobile', 'android', 'local.properties') + + # Parse version to get version code + # Convert X.Y.Z to version code: X*10000 + Y*100 + Z + try: + parts = version.split('.') + if len(parts) >= 3: + major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2]) + version_code = major * 10000 + minor * 100 + patch + elif len(parts) == 2: + major, minor = int(parts[0]), int(parts[1]) + version_code = major * 10000 + minor * 100 + else: + version_code = int(parts[0]) * 10000 + except ValueError: + print(f"WARNING: Could not parse version {version}, using default version code") + version_code = 1 + + try: + # Read existing local.properties or create new + properties = {} + if os.path.exists(local_properties_path): + with open(local_properties_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if '=' in line and not line.startswith('#'): + key, value = line.split('=', 1) + properties[key.strip()] = value.strip() + + # Update version properties + old_version_code = properties.get('flutter.versionCode', 'unknown') + old_version_name = properties.get('flutter.versionName', 'unknown') + + properties['flutter.versionCode'] = str(version_code) + properties['flutter.versionName'] = version + + # Write back to file + with open(local_properties_path, 'w', encoding='utf-8') as f: + for key, value in properties.items(): + f.write(f"{key}={value}\n") + + print(f"Updated mobile/android/local.properties:") + print(f" versionCode: {old_version_code} -> {version_code}") + print(f" versionName: {old_version_name} -> {version}") + return True + except Exception as e: + print(f"WARNING: Failed to update local.properties: {e}") + print("This is not critical - Flutter will use pubspec.yaml version") + return False + +def main(): + """Main function""" + print("Syncing version from setup.py to mobile app...") + + # Get version from setup.py + version = get_version_from_setup() + print(f"Found version in setup.py: {version}") + + # Update pubspec.yaml + update_pubspec_yaml(version) + + # Update Android build configuration + update_android_build_gradle(version) + + print("Version sync complete!") + +if __name__ == '__main__': + main()