From 47b3cc4d4072a17f3ffee7cb904959d80bd3af03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Tue, 21 Apr 2026 18:26:39 +0800 Subject: [PATCH] Reduce Windows installer confusion and heuristic AV exposure Windows releases were shipping both NSIS exe and MSI bundles, which made the release page harder to understand and increased the chance that users would pick the wrong asset. Restricting Windows output to MSI narrows the installer surface while the release asset naming now spells out platform, architecture, and bundle type directly in every published filename. Constraint: Remote desktop packaging is triggered by GitHub Actions releases, not local uploads Constraint: Updater metadata must keep latest.json stable for existing clients Rejected: Keep publishing both NSIS and MSI with better docs | still leaves the higher-risk installer on the release page Rejected: Rename assets manually after upload | easy to drift from workflow output and updater metadata Confidence: high Scope-risk: moderate Reversibility: clean Directive: If Windows exe bundles are reintroduced later, reassess SmartScreen and AV impact before publishing them again Tested: YAML parse for both desktop workflows; git diff --check on modified files; manual review of PowerShell changes Not-tested: End-to-end GitHub Actions release run; PowerShell parser validation on a Windows host --- .github/workflows/build-desktop-dev.yml | 43 ++++++++++++++++++++++--- .github/workflows/release-desktop.yml | 9 +++++- desktop/README.md | 4 +-- desktop/scripts/build-windows-x64.ps1 | 37 +++++++++++---------- 4 files changed, 69 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-desktop-dev.yml b/.github/workflows/build-desktop-dev.yml index 8a0ac379..517bb161 100644 --- a/.github/workflows/build-desktop-dev.yml +++ b/.github/workflows/build-desktop-dev.yml @@ -33,7 +33,7 @@ jobs: steps: - id: set-matrix run: | - ALL='[{"platform":"macos-latest","rust_target":"aarch64-apple-darwin","tauri_args":"--target aarch64-apple-darwin","bundles":"app,dmg","label":"macOS-ARM64","artifact_name":"macos-arm64"},{"platform":"macos-latest","rust_target":"x86_64-apple-darwin","tauri_args":"--target x86_64-apple-darwin","bundles":"app,dmg","label":"macOS-x64","artifact_name":"macos-x64"},{"platform":"windows-latest","rust_target":"x86_64-pc-windows-msvc","tauri_args":"","bundles":"nsis,msi","label":"Windows-x64","artifact_name":"windows-x64"},{"platform":"ubuntu-22.04","rust_target":"x86_64-unknown-linux-gnu","tauri_args":"","bundles":"deb","label":"Linux-x64","artifact_name":"linux-x64"},{"platform":"ubuntu-22.04-arm","rust_target":"aarch64-unknown-linux-gnu","tauri_args":"","bundles":"deb","label":"Linux-ARM64","artifact_name":"linux-arm64"}]' + ALL='[{"platform":"macos-latest","rust_target":"aarch64-apple-darwin","tauri_args":"--target aarch64-apple-darwin","bundles":"app,dmg","label":"macOS-ARM64","artifact_name":"macos-arm64","asset_platform":"macos","asset_arch":"arm64"},{"platform":"macos-latest","rust_target":"x86_64-apple-darwin","tauri_args":"--target x86_64-apple-darwin","bundles":"app,dmg","label":"macOS-x64","artifact_name":"macos-x64","asset_platform":"macos","asset_arch":"x64"},{"platform":"windows-latest","rust_target":"x86_64-pc-windows-msvc","tauri_args":"","bundles":"msi","label":"Windows-x64","artifact_name":"windows-x64","asset_platform":"windows","asset_arch":"x64"},{"platform":"ubuntu-22.04","rust_target":"x86_64-unknown-linux-gnu","tauri_args":"","bundles":"deb","label":"Linux-x64","artifact_name":"linux-x64","asset_platform":"linux","asset_arch":"x64"},{"platform":"ubuntu-22.04-arm","rust_target":"aarch64-unknown-linux-gnu","tauri_args":"","bundles":"deb","label":"Linux-ARM64","artifact_name":"linux-arm64","asset_platform":"linux","asset_arch":"arm64"}]' SELECTED="${{ inputs.platforms }}" if [ "$SELECTED" = "all" ]; then @@ -169,12 +169,47 @@ jobs: if [ -d "$SEARCH_DIR" ]; then find "$SEARCH_DIR" \ \( -type f \( \ - -name "*.dmg" -o -name "*.exe" -o -name "*.msi" -o \ + -name "*.dmg" -o -name "*.msi" -o \ -name "*.deb" -o -name "*.AppImage" -o -name "*.zip" \ \) \) -o \ \( -type d -name "*.app" \) | while read -r f; do - echo " Copying: $(basename "$f")" - cp -R "$f" "$STAGING/" + base="$(basename "$f")" + bundle="artifact" + ext="" + + case "$base" in + *.app) + bundle="app" + ext=".app" + ;; + *.dmg) + bundle="dmg" + ext=".dmg" + ;; + *.msi) + bundle="msi" + ext=".msi" + ;; + *.deb) + bundle="deb" + ext=".deb" + ;; + *.AppImage) + bundle="appimage" + ext=".AppImage" + ;; + *.zip) + bundle="zip" + ext=".zip" + ;; + *) + ext="" + ;; + esac + + target="Claude-Code-Haha_${{ steps.version.outputs.version }}_${{ matrix.asset_platform }}_${{ matrix.asset_arch }}_${bundle}${ext}" + echo " Copying: ${base} -> ${target}" + cp -R "$f" "$STAGING/$target" done fi diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 6603eef0..29652616 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -29,30 +29,35 @@ jobs: rust_target: aarch64-apple-darwin tauri_args: '--target aarch64-apple-darwin' label: macOS-ARM64 + asset_name_pattern: 'Claude-Code-Haha_[version]_macos_arm64_[bundle][ext]' # macOS Intel - platform: macos-latest rust_target: x86_64-apple-darwin tauri_args: '--target x86_64-apple-darwin' label: macOS-x64 + asset_name_pattern: 'Claude-Code-Haha_[version]_macos_x64_[bundle][ext]' # Linux x64 - platform: ubuntu-22.04 rust_target: x86_64-unknown-linux-gnu tauri_args: '--bundles deb' label: Linux-x64 + asset_name_pattern: 'Claude-Code-Haha_[version]_linux_x64_[bundle][ext]' # Linux ARM64 - platform: ubuntu-22.04-arm rust_target: aarch64-unknown-linux-gnu tauri_args: '--bundles deb' label: Linux-ARM64 + asset_name_pattern: 'Claude-Code-Haha_[version]_linux_arm64_[bundle][ext]' # Windows x64 - platform: windows-latest rust_target: x86_64-pc-windows-msvc - tauri_args: '' + tauri_args: '--bundles msi' label: Windows-x64 + asset_name_pattern: 'Claude-Code-Haha_[version]_windows_x64_[bundle][ext]' runs-on: ${{ matrix.platform }} name: Build (${{ matrix.label }}) @@ -171,4 +176,6 @@ jobs: releaseBody: ${{ steps.release_notes.outputs.body }} releaseDraft: ${{ github.event_name == 'workflow_dispatch' && inputs.draft || false }} prerelease: false + updaterJsonPreferNsis: false + releaseAssetNamePattern: ${{ matrix.asset_name_pattern }} args: ${{ matrix.tauri_args }} --config src-tauri/tauri.release-ci.json diff --git a/desktop/README.md b/desktop/README.md index eeb617ba..dfecefeb 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -15,11 +15,11 @@ bun run tauri dev # macOS (Apple Silicon) ./scripts/build-macos-arm64.sh -# Windows (x64) +# Windows (x64, MSI only) .\scripts\build-windows-x64.ps1 ``` -构建产物位于 `build-artifacts/` 目录。 +构建产物位于 `build-artifacts/` 目录,文件名会显式包含平台、架构和包类型。 ## 常见问题 diff --git a/desktop/scripts/build-windows-x64.ps1 b/desktop/scripts/build-windows-x64.ps1 index 7a7d0cb1..ffd72f68 100644 --- a/desktop/scripts/build-windows-x64.ps1 +++ b/desktop/scripts/build-windows-x64.ps1 @@ -15,6 +15,7 @@ $targetTriple = 'x86_64-pc-windows-msvc' $tauriTargetDir = Join-Path $desktopDir 'src-tauri\target' $canonicalOutputDir = Join-Path $desktopDir 'build-artifacts\windows-x64' $activeOutputDir = $canonicalOutputDir +$appVersion = (Get-Content -Path (Join-Path $desktopDir 'src-tauri\tauri.conf.json') -Raw | ConvertFrom-Json).version function Write-Step { param([string]$Message) @@ -107,6 +108,19 @@ function Get-LatestArtifact { return $null } +function Get-StagedArtifactName { + param([string]$ArtifactName) + + switch -Regex ($ArtifactName) { + '^latest\.json$' { return 'latest.json' } + '\.msi\.zip\.sig$' { return "Claude-Code-Haha_${appVersion}_windows_x64_msi.msi.zip.sig" } + '\.msi\.zip$' { return "Claude-Code-Haha_${appVersion}_windows_x64_msi.msi.zip" } + '\.msi\.sig$' { return "Claude-Code-Haha_${appVersion}_windows_x64_msi.msi.sig" } + '\.msi$' { return "Claude-Code-Haha_${appVersion}_windows_x64_msi.msi" } + default { return $ArtifactName } + } +} + function Resolve-OutputDirectory { param([string]$PreferredPath) @@ -181,7 +195,7 @@ $tauriBuildArgs = @( '--target', $targetTriple, '--bundles', - 'nsis,msi', + 'msi', '--ci' ) @@ -228,7 +242,7 @@ $bundleRoots = @( (Join-Path $tauriTargetDir 'release\bundle') ) -$artifactPatterns = @('*.exe', '*.msi', '*.zip', '*.sig', 'latest.json') +$artifactPatterns = @('*.msi', '*.msi.sig', '*.msi.zip', '*.msi.zip.sig', 'latest.json') $copiedArtifacts = New-Object System.Collections.Generic.List[string] foreach ($root in $bundleRoots) { @@ -239,7 +253,8 @@ foreach ($root in $bundleRoots) { foreach ($pattern in $artifactPatterns) { $artifacts = Get-ChildItem -Path $root -Recurse -File -Filter $pattern -ErrorAction SilentlyContinue foreach ($artifact in $artifacts) { - $destination = Join-Path $activeOutputDir $artifact.Name + $destinationName = Get-StagedArtifactName -ArtifactName $artifact.Name + $destination = Join-Path $activeOutputDir $destinationName Copy-Item -LiteralPath $artifact.FullName -Destination $destination -Force if (-not $copiedArtifacts.Contains($destination)) { $copiedArtifacts.Add($destination) | Out-Null @@ -248,25 +263,19 @@ foreach ($root in $bundleRoots) { } } -$nsisInstaller = Get-LatestArtifact -SearchRoots @( - (Join-Path $tauriTargetDir "$targetTriple\release\bundle\nsis"), - (Join-Path $tauriTargetDir 'release\bundle\nsis') -) -Patterns @('*.exe') - $msiInstaller = Get-LatestArtifact -SearchRoots @( (Join-Path $tauriTargetDir "$targetTriple\release\bundle\msi"), (Join-Path $tauriTargetDir 'release\bundle\msi') ) -Patterns @('*.msi') -$nsisInstallerPath = if ($nsisInstaller) { $nsisInstaller.FullName } else { 'not found' } $msiInstallerPath = if ($msiInstaller) { $msiInstaller.FullName } else { 'not found' } $buildInfo = @( + "App version: $appVersion" "Target triple: $targetTriple" "Canonical output: $canonicalOutputDir" "Actual output: $activeOutputDir" - "NSIS installer: $nsisInstallerPath" - "MSI installer: $msiInstallerPath" + "Windows installer (MSI): $msiInstallerPath" "Artifacts copied: $($copiedArtifacts.Count)" "Built at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')" ) @@ -276,12 +285,6 @@ Set-Content -Path (Join-Path $activeOutputDir 'BUILD_INFO.txt') -Value $buildInf Write-Host '' Write-Step 'Build finished.' Write-Step "Artifacts output: $activeOutputDir" -if ($nsisInstaller) { - Write-Step "NSIS installer source: $($nsisInstaller.FullName)" -} else { - Write-Step 'No NSIS installer found under bundle directories.' -} - if ($msiInstaller) { Write-Step "MSI installer source: $($msiInstaller.FullName)" } else {