# ============================================================================= # Wraith — Build & Sign Release (Tauri v2) # ============================================================================= name: Build & Sign Wraith on: push: tags: - 'v*' workflow_dispatch: env: EXTRA_PATH: C:\Program Files (x86)\NSIS;C:\Program Files\Eclipse Adoptium\jre-21.0.10.7-hotspot\bin;C:\Users\vantz\.cargo\bin;C:\Users\vantz\.rustup\toolchains\stable-x86_64-pc-windows-msvc\bin;C:\Program Files\nodejs jobs: build-and-sign: name: Build Windows + Sign runs-on: windows steps: - name: Checkout code shell: powershell run: | git clone --depth 1 --branch ${{ github.ref_name }} https://${{ secrets.GIT_TOKEN }}@git.command.vigilcyber.com/vstockwell/wraith.git . - name: Configure Rust shell: powershell run: | $env:Path = "$env:EXTRA_PATH;$env:Path" $ErrorActionPreference = "Continue" rustup default stable $ErrorActionPreference = "Stop" - name: Verify toolchain shell: powershell run: | $env:Path = "$env:EXTRA_PATH;$env:Path" node --version rustc --version cargo --version java --version - name: Patch version from git tag shell: powershell run: | $ver = ("${{ github.ref_name }}" -replace '^v','') $conf = Get-Content src-tauri\tauri.conf.json -Raw $conf = $conf -replace '"version":\s*"[^"]*"', "`"version`": `"$ver`"" [System.IO.File]::WriteAllText((Join-Path (Get-Location) "src-tauri\tauri.conf.json"), $conf) Write-Host "Patched tauri.conf.json version to $ver" - name: Install dependencies and build frontend shell: powershell run: | $env:Path = "$env:EXTRA_PATH;$env:Path" npm ci npm run build - name: Install Tauri CLI shell: powershell run: | $env:Path = "$env:EXTRA_PATH;$env:Path" cargo install tauri-cli --version "^2" - name: Build Tauri app (with update signing) shell: powershell run: | $env:Path = "$env:EXTRA_PATH;$env:Path" $env:TAURI_SIGNING_PRIVATE_KEY = "${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}" $env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD = "${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}" cargo tauri build Write-Host "=== Build output ===" Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\* - name: Download jsign shell: powershell run: | Invoke-WebRequest -Uri "https://github.com/ebourg/jsign/releases/download/7.0/jsign-7.0.jar" -OutFile jsign.jar - name: Get Azure token shell: powershell run: | $body = @{ client_id = "${{ secrets.AZURE_CLIENT_ID }}" client_secret = "${{ secrets.AZURE_CLIENT_SECRET }}" scope = "https://vault.azure.net/.default" grant_type = "client_credentials" } $resp = Invoke-RestMethod -Uri "https://login.microsoftonline.com/${{ secrets.AZURE_TENANT_ID }}/oauth2/v2.0/token" -Method POST -Body $body $token = $resp.access_token echo "::add-mask::$token" [System.IO.File]::WriteAllText("$env:TEMP\aztoken.txt", $token) - name: Sign binaries shell: powershell run: | $env:Path = "$env:EXTRA_PATH;$env:Path" $token = [System.IO.File]::ReadAllText("$env:TEMP\aztoken.txt") $binaries = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe foreach ($binary in $binaries) { Write-Host "Signing: $($binary.FullName)" java -jar jsign.jar --storetype AZUREKEYVAULT --keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" --storepass $token --alias "${{ secrets.AZURE_CERT_NAME }}" --tsaurl http://timestamp.digicert.com --tsmode RFC3161 $binary.FullName Write-Host "Signed: $($binary.Name)" } Remove-Item "$env:TEMP\aztoken.txt" -ErrorAction SilentlyContinue - name: Upload to Gitea shell: powershell run: | $ver = ("${{ github.ref_name }}" -replace '^v','') $giteaUrl = "https://git.command.vigilcyber.com" $headers = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}" } $installers = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe foreach ($file in $installers) { $hash = (Get-FileHash $file.FullName -Algorithm SHA256).Hash.ToLower() @{ version = $ver; filename = $file.Name; sha256 = $hash; platform = "windows"; architecture = "amd64"; released = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"); signed = $true } | ConvertTo-Json | Out-File version.json -Encoding utf8 Write-Host "Uploading: $($file.Name)" Invoke-RestMethod -Uri "$giteaUrl/api/packages/vstockwell/generic/wraith/$ver/$($file.Name)" -Method PUT -Headers $headers -ContentType "application/octet-stream" -InFile $file.FullName Write-Host "Uploading: version.json" Invoke-RestMethod -Uri "$giteaUrl/api/packages/vstockwell/generic/wraith/$ver/version.json" -Method PUT -Headers $headers -ContentType "application/octet-stream" -InFile version.json } Write-Host "=== Upload complete ===" - name: Generate and upload update.json for Tauri updater shell: powershell run: | $ver = ("${{ github.ref_name }}" -replace '^v','') $giteaUrl = "https://git.command.vigilcyber.com" $headers = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}" } # Find the .sig file produced by Tauri signing $sigFile = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.nsis.zip.sig | Select-Object -First 1 $zipFile = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.nsis.zip | Select-Object -First 1 if ($sigFile -and $zipFile) { $signature = Get-Content $sigFile.FullName -Raw $downloadUrl = "$giteaUrl/api/packages/vstockwell/generic/wraith/$ver/$($zipFile.Name)" # Upload the .nsis.zip to packages Write-Host "Uploading: $($zipFile.Name)" Invoke-RestMethod -Uri "$giteaUrl/api/packages/vstockwell/generic/wraith/$ver/$($zipFile.Name)" -Method PUT -Headers $headers -ContentType "application/octet-stream" -InFile $zipFile.FullName # Build update.json $updateJson = @{ version = "v$ver" notes = "Wraith Desktop v$ver" pub_date = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ") platforms = @{ "windows-x86_64" = @{ signature = $signature.Trim() url = $downloadUrl } } } | ConvertTo-Json -Depth 4 $updateJson | Out-File update.json -Encoding utf8 Write-Host "update.json content:" Get-Content update.json # Upload to latest/ so the updater endpoint always points to the newest Invoke-RestMethod -Uri "$giteaUrl/api/packages/vstockwell/generic/wraith/latest/update.json" -Method PUT -Headers $headers -ContentType "application/octet-stream" -InFile update.json # Also upload to versioned path Invoke-RestMethod -Uri "$giteaUrl/api/packages/vstockwell/generic/wraith/$ver/update.json" -Method PUT -Headers $headers -ContentType "application/octet-stream" -InFile update.json Write-Host "=== Update manifest uploaded ===" } else { Write-Host "WARNING: No .sig file found — update signing may have failed" Write-Host "Sig files found:" Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.sig } - name: Create Release and attach installers shell: powershell run: | $ver = ("${{ github.ref_name }}" -replace '^v','') $giteaUrl = "https://git.command.vigilcyber.com" $headers = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}"; "Content-Type" = "application/json" } $body = @{ tag_name = "v$ver"; name = "Wraith v$ver"; body = "Wraith Desktop v$ver - Tauri v2 / Rust build." } | ConvertTo-Json $release = Invoke-RestMethod -Uri "$giteaUrl/api/v1/repos/vstockwell/wraith/releases" -Method POST -Headers $headers -Body $body $releaseId = $release.id Write-Host "Release v$ver created (id: $releaseId)" $installers = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe $uploadHeaders = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}" } foreach ($file in $installers) { Write-Host "Attaching $($file.Name) to release..." Invoke-RestMethod -Uri "$giteaUrl/api/v1/repos/vstockwell/wraith/releases/$releaseId/assets?name=$($file.Name)" -Method POST -Headers $uploadHeaders -ContentType "application/octet-stream" -InFile $file.FullName Write-Host "Attached: $($file.Name)" }