name: "Bundle Desktop (Windows)" on: workflow_dispatch: inputs: signing: description: 'Whether to sign the Windows executable' required: false type: boolean default: false windows_variant: description: 'Windows artifact variant to build' required: false type: string default: 'standard' workflow_call: inputs: version: description: 'Version to build' required: false type: string signing: description: 'Whether to sign the Windows executable' required: false type: boolean default: false ref: description: 'Git ref to checkout' required: false type: string default: '' windows_variant: description: 'Windows artifact variant to build' required: false type: string default: 'standard' # Permissions required for OIDC authentication with Azure Trusted Signing permissions: id-token: write # Required to fetch the OIDC token for Azure federated credentials contents: read # Required by actions/checkout actions: read # May be needed for some workflows jobs: build-desktop-windows: name: Build Desktop (Windows) runs-on: windows-latest steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.ref != '' && inputs.ref || '' }} - name: Set up Node.js uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 24.10.0 - name: Install pnpm run: npm install -g pnpm@10.30.3 - name: Cache node_modules uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 with: path: | node_modules ui/desktop/node_modules .hermit/node/cache key: windows-pnpm-cache-v1-${{ runner.os }}-node24-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | windows-pnpm-cache-v1-${{ runner.os }}-node24- - name: Cache Rust dependencies uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: key: windows-msvc-desktop-${{ inputs.windows_variant }} - name: Setup Rust shell: bash run: | rustup show rustup target add x86_64-pc-windows-msvc - name: Install CUDA toolkit (Windows CUDA) if: ${{ inputs.windows_variant == 'cuda' }} uses: Jimver/cuda-toolkit@v0.2.35 with: cuda: '12.9.1' method: 'local' log-file-suffix: 'bundle-desktop-windows-cuda.txt' - name: Set up MSVC developer environment (Windows CUDA) if: ${{ inputs.windows_variant == 'cuda' }} uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 with: arch: amd64 - name: Verify CUDA toolchain (Windows CUDA) if: ${{ inputs.windows_variant == 'cuda' }} shell: pwsh env: CUDA_COMPUTE_CAP: "80" run: | Write-Output "CUDA_PATH=$env:CUDA_PATH" Write-Output "CUDA_COMPUTE_CAP=$env:CUDA_COMPUTE_CAP" where.exe cl where.exe nvcc nvcc -V - name: Build Windows executable shell: pwsh env: CUDA_COMPUTE_CAP: ${{ inputs.windows_variant == 'cuda' && '80' || '' }} run: | Write-Output "Building Windows executable..." if ("${{ inputs.windows_variant }}" -eq "cuda") { cargo build --release --target x86_64-pc-windows-msvc -p goose-server --features cuda } else { cargo build --release --target x86_64-pc-windows-msvc -p goose-server } # Verify build succeeded if (-not (Test-Path "./target/x86_64-pc-windows-msvc/release/goosed.exe")) { Write-Error "Windows binary not found." Get-ChildItem ./target/x86_64-pc-windows-msvc/release/ -ErrorAction SilentlyContinue exit 1 } Write-Output "Windows binary found." Get-Item ./target/x86_64-pc-windows-msvc/release/goosed.exe - name: Prepare Windows binary shell: bash run: | if [ ! -f "./target/x86_64-pc-windows-msvc/release/goosed.exe" ]; then echo "Windows binary not found." exit 1 fi echo "Cleaning destination directory..." rm -rf ./ui/desktop/src/bin mkdir -p ./ui/desktop/src/bin echo "Copying Windows binary..." cp -f ./target/x86_64-pc-windows-msvc/release/goosed.exe ./ui/desktop/src/bin/ if [ -d "./ui/desktop/src/platform/windows/bin" ]; then echo "Copying Windows platform files..." for file in ./ui/desktop/src/platform/windows/bin/*.{exe,dll,cmd}; do if [ -f "$file" ] && [ "$(basename "$file")" != "goosed.exe" ]; then cp -f "$file" ./ui/desktop/src/bin/ fi done if [ -d "./ui/desktop/src/platform/windows/bin/goose-npm" ]; then echo "Setting up npm environment..." cp -r ./ui/desktop/src/platform/windows/bin/goose-npm/ ./ui/desktop/src/bin/goose-npm/ fi echo "Windows-specific files copied successfully" fi - name: Force GitHub HTTPS for npm git dependencies shell: bash run: | git config --global url."https://github.com/".insteadOf "ssh://git@github.com/" git config --global url."https://github.com/".insteadOf "git@github.com:" git config --global url."https://github.com/".insteadOf "git+ssh://git@github.com/" - name: Build desktop UI with pnpm shell: bash env: ELECTRON_PLATFORM: win32 run: | cd ui/desktop pnpm install --frozen-lockfile node scripts/build-main.js node scripts/prepare-platform-binaries.js pnpm run make --platform=win32 --arch=x64 - name: Copy exe to final out folder and prepare flat distribution shell: bash run: | cd ui/desktop mkdir -p ./out/Goose-win32-x64/resources/bin cp -r src/bin/* out/Goose-win32-x64/resources/bin/ mkdir -p ./dist-windows cp -r ./out/Goose-win32-x64/* ./dist-windows/ echo "📋 Final flat distribution structure:" ls -la ./dist-windows/ echo "📋 Binary files in resources/bin:" ls -la ./dist-windows/resources/bin/ - name: Upload unsigned distribution uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: windows-unsigned${{ inputs.windows_variant == 'cuda' && '-cuda' || '' }} path: ui/desktop/dist-windows/ sign-desktop-windows: name: Sign Desktop (Windows) needs: build-desktop-windows if: inputs.signing runs-on: windows-latest environment: ${{ inputs.signing && 'signing' || null }} steps: - name: Download unsigned distribution uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: windows-unsigned${{ inputs.windows_variant == 'cuda' && '-cuda' || '' }} path: dist-windows - name: Azure login uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Sign Windows executables with Azure Trusted Signing uses: azure/trusted-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0 with: endpoint: ${{ secrets.AZURE_SIGNING_ENDPOINT }} trusted-signing-account-name: ${{ secrets.AZURE_SIGNING_ACCOUNT_NAME }} certificate-profile-name: ${{ secrets.AZURE_CERTIFICATE_PROFILE_NAME }} files: | ${{ github.workspace }}/dist-windows/Goose.exe ${{ github.workspace }}/dist-windows/resources/bin/goosed.exe - name: Verify signed executables shell: pwsh run: | $files = @( "dist-windows/Goose.exe", "dist-windows/resources/bin/goosed.exe" ) foreach ($file in $files) { Write-Output "Verifying signature: $file" $sig = Get-AuthenticodeSignature $file if ($sig.Status -ne "Valid") { throw "Signature invalid for ${file}: $($sig.Status)" } Write-Output "Signature valid: $file" } - name: Create Windows zip package shell: bash run: | ZIP_NAME="Goose-win32-x64" if [ "${{ inputs.windows_variant }}" = "cuda" ]; then ZIP_NAME="${ZIP_NAME}-cuda" fi 7z a -tzip "${ZIP_NAME}.zip" dist-windows/ - name: Upload signed Windows build uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Goose-win32-x64${{ inputs.windows_variant == 'cuda' && '-cuda' || '' }} path: Goose-win32-x64${{ inputs.windows_variant == 'cuda' && '-cuda' || '' }}.zip # When signing is disabled, package the unsigned build directly package-desktop-windows: name: Package Desktop (Windows) needs: build-desktop-windows if: ${{ !inputs.signing }} runs-on: windows-latest steps: - name: Download unsigned distribution uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: windows-unsigned${{ inputs.windows_variant == 'cuda' && '-cuda' || '' }} path: dist-windows - name: Create Windows zip package shell: bash run: | ZIP_NAME="Goose-win32-x64" if [ "${{ inputs.windows_variant }}" = "cuda" ]; then ZIP_NAME="${ZIP_NAME}-cuda" fi 7z a -tzip "${ZIP_NAME}.zip" dist-windows/ - name: Upload Windows build uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Goose-win32-x64${{ inputs.windows_variant == 'cuda' && '-cuda' || '' }} path: Goose-win32-x64${{ inputs.windows_variant == 'cuda' && '-cuda' || '' }}.zip