# ============================================================================= # Wraith — Build & Sign Release (Tauri v2) # ============================================================================= # Native Windows build on STORMBREAKER runner, signs with Azure Key Vault # EV cert via jsign, creates NSIS installer, uploads to Gitea packages. # # Trigger: push a tag matching v* (e.g. v1.0.0) or run manually. # # Required secrets: # AZURE_TENANT_ID — Azure AD tenant # AZURE_CLIENT_ID — Service principal client ID # AZURE_CLIENT_SECRET — Service principal secret # AZURE_KEY_VAULT_URL — e.g. https://my-vault.vault.azure.net # AZURE_CERT_NAME — Certificate/key name in the vault # GIT_TOKEN — PAT for cloning private repo + uploading packages # TAURI_SIGNING_PRIVATE_KEY — Tauri updater signing key (base64) # TAURI_SIGNING_PRIVATE_KEY_PASSWORD — Password for the signing key # ============================================================================= name: Build & Sign Wraith on: push: tags: - 'v*' workflow_dispatch: jobs: build-and-sign: name: Build Windows + Sign runs-on: windows steps: # --------------------------------------------------------------- # Checkout # --------------------------------------------------------------- - 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 . # --------------------------------------------------------------- # Extract version from tag # --------------------------------------------------------------- - name: Get version from tag id: version shell: powershell run: | $tag = "${{ github.ref_name }}" -replace '^v','' echo "version=$tag" >> $env:GITHUB_OUTPUT Write-Host "Building version: $tag" # --------------------------------------------------------------- # Verify toolchain # --------------------------------------------------------------- - name: Verify toolchain shell: powershell run: | Write-Host "=== Toolchain versions ===" node --version rustc --version cargo --version java --version python --version # --------------------------------------------------------------- # Build frontend # --------------------------------------------------------------- - name: Install frontend dependencies shell: powershell run: npm ci - name: Build frontend shell: powershell run: | npm run build Write-Host "=== Frontend built ===" Get-ChildItem dist\ # --------------------------------------------------------------- # Build Tauri app (native MSVC) # --------------------------------------------------------------- - name: Install Tauri CLI shell: powershell run: cargo install tauri-cli --version "^2" - name: Build Tauri app shell: powershell env: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} run: | cargo tauri build Write-Host "=== Build output ===" Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe # --------------------------------------------------------------- # Code signing — jsign + Azure Key Vault (EV cert) # --------------------------------------------------------------- - 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 Key Vault access token id: 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" echo "token=$token" >> $env:GITHUB_OUTPUT - name: Sign Windows binaries shell: powershell run: | Write-Host "=== Signing Wraith binaries with EV certificate ===" $installers = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe foreach ($binary in $installers) { Write-Host "Signing: $($binary.FullName)" java -jar jsign.jar ` --storetype AZUREKEYVAULT ` --keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" ` --storepass "${{ steps.azure-token.outputs.token }}" ` --alias "${{ secrets.AZURE_CERT_NAME }}" ` --tsaurl http://timestamp.digicert.com ` --tsmode RFC3161 ` $binary.FullName Write-Host "Signed: $($binary.Name)" } # --------------------------------------------------------------- # Create version.json # --------------------------------------------------------------- - name: Create version.json shell: powershell run: | $version = "${{ steps.version.outputs.version }}" $installer = (Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe | Select-Object -First 1) $hash = (Get-FileHash $installer.FullName -Algorithm SHA256).Hash.ToLower() $json = @{ version = $version filename = $installer.Name sha256 = $hash platform = "windows" architecture = "amd64" released = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ") signed = $true } | ConvertTo-Json $json | Out-File -FilePath version.json -Encoding utf8 Write-Host "=== version.json ===" Get-Content version.json # --------------------------------------------------------------- # Upload to Gitea Package Registry # --------------------------------------------------------------- - name: Upload to Gitea packages shell: powershell run: | $version = "${{ steps.version.outputs.version }}" $giteaUrl = "https://git.command.vigilcyber.com" $owner = "vstockwell" $package = "wraith" $headers = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}" } Write-Host "=== Uploading Wraith v$version to Gitea packages ===" # Upload installer(s) $installers = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe foreach ($file in $installers) { Write-Host "Uploading: $($file.Name)" Invoke-RestMethod -Uri "$giteaUrl/api/packages/$owner/generic/$package/$version/$($file.Name)" ` -Method PUT -Headers $headers -ContentType "application/octet-stream" ` -InFile $file.FullName } # Upload version.json Write-Host "Uploading: version.json" Invoke-RestMethod -Uri "$giteaUrl/api/packages/$owner/generic/$package/$version/version.json" ` -Method PUT -Headers $headers -ContentType "application/octet-stream" ` -InFile version.json # Upload updater signature if exists $sigs = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.sig -ErrorAction SilentlyContinue foreach ($sig in $sigs) { Write-Host "Uploading: $($sig.Name)" Invoke-RestMethod -Uri "$giteaUrl/api/packages/$owner/generic/$package/$version/$($sig.Name)" ` -Method PUT -Headers $headers -ContentType "application/octet-stream" ` -InFile $sig.FullName } Write-Host "" Write-Host "=== Upload complete ===" # --------------------------------------------------------------- # Create Gitea Release # --------------------------------------------------------------- - name: Create Gitea Release shell: powershell run: | $version = "${{ steps.version.outputs.version }}" $giteaUrl = "https://git.command.vigilcyber.com" $headers = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}" "Content-Type" = "application/json" } $body = @{ tag_name = "v$version" name = "Wraith v$version" body = "Wraith Desktop v$version — Tauri v2 / Rust build." } | ConvertTo-Json Invoke-RestMethod -Uri "$giteaUrl/api/v1/repos/vstockwell/wraith/releases" ` -Method POST -Headers $headers -Body $body Write-Host "Release created."