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
This commit is contained in:
程序员阿江(Relakkes) 2026-04-21 18:26:39 +08:00
parent 056dc36331
commit 47b3cc4d40
4 changed files with 69 additions and 24 deletions

View File

@ -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

View File

@ -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

View File

@ -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/` 目录,文件名会显式包含平台、架构和包类型
## 常见问题

View File

@ -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 {