diff --git a/.github/scripts/prefetch-tauri-nsis.ps1 b/.github/scripts/prefetch-tauri-nsis.ps1 new file mode 100644 index 00000000..79da404b --- /dev/null +++ b/.github/scripts/prefetch-tauri-nsis.ps1 @@ -0,0 +1,137 @@ +$ErrorActionPreference = "Stop" + +$nsisUrl = "https://github.com/tauri-apps/binary-releases/releases/download/nsis-3.11/nsis-3.11.zip" +$nsisSha1 = "EF7FF767E5CBD9EDD22ADD3A32C9B8F4500BB10D" +$tauriUtilsUrl = "https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.5.3/nsis_tauri_utils.dll" +$tauriUtilsSha1 = "75197FEE3C6A814FE035788D1C34EAD39349B860" +$tauriUtilsRelativePath = "Plugins\x86-unicode\additional\nsis_tauri_utils.dll" + +$nsisRequiredFiles = @( + "makensis.exe", + "Bin\makensis.exe", + "Stubs\lzma-x86-unicode", + "Stubs\lzma_solid-x86-unicode", + "Include\MUI2.nsh", + "Include\FileFunc.nsh", + "Include\x64.nsh", + "Include\nsDialogs.nsh", + "Include\WinMessages.nsh", + "Include\Win\COM.nsh", + "Include\Win\Propkey.nsh", + "Include\Win\RestartManager.nsh" +) + +function Get-UpperSha1 { + param([Parameter(Mandatory = $true)][string]$Path) + + return (Get-FileHash -Algorithm SHA1 -LiteralPath $Path).Hash.ToUpperInvariant() +} + +function Test-FileSha1 { + param( + [Parameter(Mandatory = $true)][string]$Path, + [Parameter(Mandatory = $true)][string]$ExpectedSha1 + ) + + return (Test-Path -LiteralPath $Path) -and ((Get-UpperSha1 -Path $Path) -eq $ExpectedSha1) +} + +function Save-VerifiedDownload { + param( + [Parameter(Mandatory = $true)][string]$Uri, + [Parameter(Mandatory = $true)][string]$OutFile, + [Parameter(Mandatory = $true)][string]$ExpectedSha1 + ) + + $parent = Split-Path -Parent $OutFile + New-Item -ItemType Directory -Force -Path $parent | Out-Null + + $tempFile = "$OutFile.download" + for ($attempt = 1; $attempt -le 5; $attempt++) { + try { + Remove-Item -Force -ErrorAction SilentlyContinue -LiteralPath $tempFile + Invoke-WebRequest -Uri $Uri -OutFile $tempFile -TimeoutSec 120 + + $actualSha1 = Get-UpperSha1 -Path $tempFile + if ($actualSha1 -ne $ExpectedSha1) { + throw "SHA1 mismatch for $Uri. Expected $ExpectedSha1, got $actualSha1." + } + + Move-Item -Force -LiteralPath $tempFile -Destination $OutFile + return + } catch { + Remove-Item -Force -ErrorAction SilentlyContinue -LiteralPath $tempFile + if ($attempt -eq 5) { + throw + } + + $delaySeconds = [Math]::Min(30, 5 * $attempt) + Write-Warning "Download attempt ${attempt} failed: $($_.Exception.Message). Retrying in ${delaySeconds}s." + Start-Sleep -Seconds $delaySeconds + } + } +} + +function Find-MissingFile { + param( + [Parameter(Mandatory = $true)][string]$Root, + [Parameter(Mandatory = $true)][string[]]$RelativePaths + ) + + foreach ($relativePath in $RelativePaths) { + if (-not (Test-Path -LiteralPath (Join-Path $Root $relativePath))) { + return $relativePath + } + } + + return $null +} + +if ([string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { + throw "LOCALAPPDATA is required to resolve Tauri's Windows tool cache." +} + +$tauriToolsPath = Join-Path $env:LOCALAPPDATA "tauri" +$nsisPath = Join-Path $tauriToolsPath "NSIS" +$downloadRoot = if ([string]::IsNullOrWhiteSpace($env:RUNNER_TEMP)) { + [System.IO.Path]::GetTempPath() +} else { + $env:RUNNER_TEMP +} + +New-Item -ItemType Directory -Force -Path $tauriToolsPath | Out-Null + +$missingNsisFile = Find-MissingFile -Root $nsisPath -RelativePaths $nsisRequiredFiles +if ($missingNsisFile) { + Write-Host "Tauri NSIS cache is missing $missingNsisFile; downloading NSIS 3.11." + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -LiteralPath $nsisPath + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -LiteralPath (Join-Path $tauriToolsPath "nsis-3.11") + + $zipPath = Join-Path $downloadRoot "nsis-3.11.zip" + Save-VerifiedDownload -Uri $nsisUrl -OutFile $zipPath -ExpectedSha1 $nsisSha1 + Expand-Archive -Force -LiteralPath $zipPath -DestinationPath $tauriToolsPath + + $extractedNsisPath = Join-Path $tauriToolsPath "nsis-3.11" + if (-not (Test-Path -LiteralPath $extractedNsisPath)) { + throw "Downloaded NSIS archive did not contain the expected nsis-3.11 directory." + } + + Move-Item -Force -LiteralPath $extractedNsisPath -Destination $nsisPath +} else { + Write-Host "Tauri NSIS cache already contains NSIS 3.11." +} + +$tauriUtilsPath = Join-Path $nsisPath $tauriUtilsRelativePath +if (-not (Test-FileSha1 -Path $tauriUtilsPath -ExpectedSha1 $tauriUtilsSha1)) { + Write-Host "Downloading Tauri NSIS utility plugin." + Save-VerifiedDownload -Uri $tauriUtilsUrl -OutFile $tauriUtilsPath -ExpectedSha1 $tauriUtilsSha1 +} else { + Write-Host "Tauri NSIS utility plugin is already cached." +} + +$missingFile = Find-MissingFile -Root $nsisPath -RelativePaths ($nsisRequiredFiles + @($tauriUtilsRelativePath)) +if ($missingFile) { + throw "Tauri NSIS toolchain is incomplete after prefetch; missing $missingFile." +} + +Write-Host "Tauri NSIS toolchain ready at $nsisPath." diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml index 7e95de1c..8796e5b0 100644 --- a/.github/workflows/release-stable.yml +++ b/.github/workflows/release-stable.yml @@ -401,6 +401,16 @@ jobs: restore-keys: | ${{ runner.os }}-release-cargo-x86_64-pc-windows-msvc-${{ env.RUST_TARGET_CACHE_VERSION }}- + - name: Cache Tauri Windows tools + uses: actions/cache@v4 + with: + path: ~\AppData\Local\tauri + key: ${{ runner.os }}-tauri-tools-nsis-3.11-nsis-tauri-utils-0.5.3 + + - name: Prefetch Tauri NSIS toolchain + shell: pwsh + run: ./.github/scripts/prefetch-tauri-nsis.ps1 + - name: Install frontend dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index baccc3e2..0a327a41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -455,6 +455,16 @@ jobs: restore-keys: | ${{ runner.os }}-release-cargo-x86_64-pc-windows-msvc-${{ env.RUST_TARGET_CACHE_VERSION }}- + - name: Cache Tauri Windows tools + uses: actions/cache@v4 + with: + path: ~\AppData\Local\tauri + key: ${{ runner.os }}-tauri-tools-nsis-3.11-nsis-tauri-utils-0.5.3 + + - name: Prefetch Tauri NSIS toolchain + shell: pwsh + run: ./.github/scripts/prefetch-tauri-nsis.ps1 + - name: Install frontend dependencies run: pnpm install --frozen-lockfile