Merge pull request #40 from ruslanmv/dev-v2.0.13.8 #74
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Windows Installer | |
| on: | |
| push: | |
| branches: [ master, develop ] | |
| tags: | |
| - 'v*' | |
| pull_request: | |
| branches: [ master, develop ] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| env: | |
| RUST_BACKTRACE: 1 | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| build-windows: | |
| name: Build Windows Installer | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.10' | |
| cache: 'pip' | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| src-tauri/target/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Install Tauri CLI | |
| run: cargo install tauri-cli --version "^1.0" --force | |
| - name: Install Python dependencies | |
| run: | | |
| python -m pip install --upgrade pip wheel setuptools | |
| pip install pyinstaller | |
| pip install -e . | |
| - name: Verify and download driver installers | |
| shell: pwsh | |
| run: | | |
| $interceptionPath = "frontend/input_record/install-interception.exe" | |
| if (-not (Test-Path $interceptionPath)) { | |
| Write-Host "::warning::Interception installer not found at $interceptionPath" | |
| } else { | |
| Write-Host "✓ Interception installer found" | |
| } | |
| # Download vJoySetup.exe from official GitHub release | |
| $vjoyDir = "src-tauri/drivers/vjoy" | |
| $vjoyPath = "$vjoyDir/vJoySetup.exe" | |
| $vjoyUrl = "https://github.com/shauleiz/vJoy/releases/download/v2.1.9.1/vJoySetup.exe" | |
| New-Item -Force -ItemType Directory $vjoyDir | Out-Null | |
| if (-not (Test-Path $vjoyPath)) { | |
| Write-Host "Downloading vJoySetup.exe from official release..." | |
| try { | |
| Invoke-WebRequest -Uri $vjoyUrl -OutFile $vjoyPath -UseBasicParsing | |
| Write-Host "✓ vJoySetup.exe downloaded successfully" | |
| } catch { | |
| Write-Host "::warning::Failed to download vJoySetup.exe: $($_.Exception.Message)" | |
| Write-Host "::warning::Build will continue without vJoy driver" | |
| } | |
| } else { | |
| Write-Host "✓ vJoySetup.exe found" | |
| } | |
| - name: Build Python backend sidecar | |
| shell: pwsh | |
| run: | | |
| Write-Host "Building Python backend with PyInstaller..." | |
| pyinstaller --noconfirm --clean --onefile --name main-backend backend/main_backend.py | |
| if (-not (Test-Path "dist/main-backend.exe")) { | |
| Write-Error "PyInstaller failed to create main-backend.exe" | |
| exit 1 | |
| } | |
| Write-Host "✓ Backend sidecar built successfully" | |
| - name: Prepare Tauri sidecar binary | |
| shell: pwsh | |
| run: | | |
| $target = "x86_64-pc-windows-msvc" | |
| $tauriSidecar = "src-tauri/binaries/main-backend-$target.exe" | |
| New-Item -Force -ItemType Directory (Split-Path $tauriSidecar -Parent) | Out-Null | |
| Copy-Item -Force "dist/main-backend.exe" $tauriSidecar | |
| Write-Host "✓ Sidecar copied to: $tauriSidecar" | |
| - name: Copy driver installers and scripts | |
| shell: pwsh | |
| run: | | |
| New-Item -Force -ItemType Directory "src-tauri/drivers/interception" | Out-Null | |
| New-Item -Force -ItemType Directory "src-tauri/drivers/vjoy" | Out-Null | |
| New-Item -Force -ItemType Directory "src-tauri/resources/scripts" | Out-Null | |
| # Copy interception driver (committed to repo) | |
| Copy-Item -Force "frontend/input_record/install-interception.exe" "src-tauri/drivers/interception/install-interception.exe" | |
| # vJoySetup.exe is downloaded in the previous step; verify it exists | |
| if (Test-Path "src-tauri/drivers/vjoy/vJoySetup.exe") { | |
| Write-Host "✓ vJoySetup.exe present" | |
| } else { | |
| Write-Host "::warning::vJoySetup.exe not available — installer will skip vJoy driver" | |
| } | |
| # Copy scripts | |
| Copy-Item -Force "scripts/install_drivers.ps1" "src-tauri/resources/scripts/install_drivers.ps1" | |
| if (Test-Path "scripts/install_ml_deps.ps1") { | |
| Copy-Item -Force "scripts/install_ml_deps.ps1" "src-tauri/resources/scripts/install_ml_deps.ps1" | |
| } | |
| if (Test-Path "scripts/download_models.ps1") { | |
| Copy-Item -Force "scripts/download_models.ps1" "src-tauri/resources/scripts/download_models.ps1" | |
| } | |
| Write-Host "✓ Driver installers and scripts copied" | |
| # --- FIX: CREATE MISSING .ENV FILE BEFORE BUILD --- | |
| - name: Create default .env file | |
| shell: pwsh | |
| run: | | |
| if (-not (Test-Path ".env")) { | |
| Write-Host "Creating default .env file to satisfy bundler resources..." | |
| Set-Content -Path ".env" -Value "AI_PROVIDER=gemini" | |
| } | |
| Write-Host "✓ .env file present" | |
| - name: Build Tauri application | |
| shell: pwsh | |
| run: | | |
| Push-Location src-tauri | |
| try { | |
| cargo tauri build --verbose | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "Tauri build failed with exit code $LASTEXITCODE" | |
| exit 1 | |
| } | |
| } finally { | |
| Pop-Location | |
| } | |
| - name: Verify installer was created | |
| shell: pwsh | |
| run: | | |
| $installerDir = "src-tauri/target/release/bundle/nsis" | |
| if (-not (Test-Path $installerDir)) { | |
| Write-Error "Installer directory not found: $installerDir" | |
| exit 1 | |
| } | |
| $installers = Get-ChildItem -Path $installerDir -Filter "*.exe" | |
| if ($installers.Count -eq 0) { | |
| Write-Error "No installer executables found in $installerDir" | |
| exit 1 | |
| } | |
| foreach ($installer in $installers) { | |
| Write-Host "✓ Created: $($installer.Name) ($($installer.Length / 1MB) MB)" | |
| } | |
| - name: Upload installer artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-installer | |
| path: src-tauri/target/release/bundle/nsis/*.exe | |
| retention-days: 30 | |
| # Nightly: auto-publish EXE to a rolling "nightly" release on every master push | |
| - name: Upload installer EXE to Nightly Release (every commit on master) | |
| if: github.ref == 'refs/heads/master' | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: nightly | |
| name: Nightly (latest master build) | |
| prerelease: true | |
| generate_release_notes: false | |
| body: | | |
| ## Nightly Build | |
| **This is an automated nightly build from the latest `master` commit.** | |
| The `.exe` installer below is always up-to-date with the latest code. | |
| Download it directly — no need to unzip. | |
| ### How to install | |
| 1. Download the `.exe` file below | |
| 2. Run it with administrator privileges | |
| 3. Follow the setup wizard | |
| > **Note:** Nightly builds may contain experimental features. | |
| > For stable releases, see [tagged releases](../../releases). | |
| files: src-tauri/target/release/bundle/nsis/*.exe | |
| # Tagged releases: create a proper versioned release | |
| - name: Create Tagged Release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: src-tauri/target/release/bundle/nsis/*.exe | |
| generate_release_notes: true | |
| draft: false | |
| prerelease: false | |
| test-installer: | |
| name: Test Installer | |
| needs: build-windows | |
| runs-on: windows-latest | |
| if: "!startsWith(github.ref, 'refs/tags/')" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Download installer | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: windows-installer | |
| path: ./installer-test | |
| - name: Basic installer validation | |
| shell: pwsh | |
| run: | | |
| $installers = Get-ChildItem -Path ./installer-test -Filter "*.exe" | |
| if ($installers.Count -eq 0) { | |
| Write-Error "No installer found!" | |
| exit 1 | |
| } | |
| foreach ($installer in $installers) { | |
| Write-Host "Validating: $($installer.Name)" | |
| if ($installer.Length -lt 1MB) { | |
| Write-Warning "Installer size is suspiciously small" | |
| } | |
| # FIX: PowerShell 7 does NOT support -Encoding Byte | |
| $bytes = [System.IO.File]::ReadAllBytes($installer.FullName) | |
| $header = $bytes[0..1] | |
| if ($header[0] -eq 0x4D -and $header[1] -eq 0x5A) { | |
| Write-Host "✓ Valid PE executable header" | |
| } else { | |
| Write-Error "Invalid executable header" | |
| exit 1 | |
| } | |
| } |