Skip to content
Merged
57 changes: 49 additions & 8 deletions .github/workflows/desktop-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,16 +281,28 @@ jobs:
env:
WINDOWS_CERTIFICATE: '${{ secrets.WINDOWS_CERTIFICATE }}'
WINDOWS_CERTIFICATE_PASSWORD: '${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}'
LEGACY_WIN_CSC_LINK: '${{ secrets.WIN_CSC_LINK }}'
LEGACY_WIN_CSC_KEY_PASSWORD: '${{ secrets.WIN_CSC_KEY_PASSWORD }}'
run: |
if (-not $env:WINDOWS_CERTIFICATE -or -not $env:WINDOWS_CERTIFICATE_PASSWORD) {
throw 'WINDOWS_CERTIFICATE and WINDOWS_CERTIFICATE_PASSWORD are required for published Windows releases.'
# Prefer the Tauri-style WINDOWS_CERTIFICATE pair; fall back to the
# legacy electron-builder WIN_CSC_LINK pair if only that exists.
if ($env:WINDOWS_CERTIFICATE -and $env:WINDOWS_CERTIFICATE_PASSWORD) {
$path = Join-Path $env:RUNNER_TEMP 'qwen-code-desktop.pfx'
[IO.File]::WriteAllBytes($path, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE))
$password = ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force
$certificate = Import-PfxCertificate -FilePath $path -CertStoreLocation Cert:\CurrentUser\My -Password $password
$windowsConfig = @{ bundle = @{ windows = @{ certificateThumbprint = $certificate.Thumbprint } } } | ConvertTo-Json -Compress -Depth 3
"WINDOWS_CONFIG=$windowsConfig" | Out-File -FilePath $env:GITHUB_ENV -Append
} elseif ($env:LEGACY_WIN_CSC_LINK -and $env:LEGACY_WIN_CSC_KEY_PASSWORD) {
$path = Join-Path $env:RUNNER_TEMP 'qwen-code-desktop.pfx'
[IO.File]::WriteAllBytes($path, [Convert]::FromBase64String($env:LEGACY_WIN_CSC_LINK))
$password = ConvertTo-SecureString $env:LEGACY_WIN_CSC_KEY_PASSWORD -AsPlainText -Force
$certificate = Import-PfxCertificate -FilePath $path -CertStoreLocation Cert:\CurrentUser\My -Password $password
$windowsConfig = @{ bundle = @{ windows = @{ certificateThumbprint = $certificate.Thumbprint } } } | ConvertTo-Json -Compress -Depth 3
"WINDOWS_CONFIG=$windowsConfig" | Out-File -FilePath $env:GITHUB_ENV -Append
} else {
Write-Output "::warning::Windows signing certificate is not configured. Windows artifacts will be unsigned and may trigger SmartScreen warnings."
}
Comment thread
yiliang114 marked this conversation as resolved.
Resolved
$path = Join-Path $env:RUNNER_TEMP 'qwen-code-desktop.pfx'
[IO.File]::WriteAllBytes($path, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE))
$password = ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force
$certificate = Import-PfxCertificate -FilePath $path -CertStoreLocation Cert:\CurrentUser\My -Password $password
$windowsConfig = @{ bundle = @{ windows = @{ certificateThumbprint = $certificate.Thumbprint } } } | ConvertTo-Json -Compress -Depth 3
"WINDOWS_CONFIG=$windowsConfig" | Out-File -FilePath $env:GITHUB_ENV -Append

- name: 'Prepare bundled runtime'
working-directory: 'packages/desktop-shell'
Expand All @@ -308,6 +320,35 @@ jobs:
working-directory: 'packages/desktop-shell'
run: 'npm run test:release'

- name: 'Sign bundled vendor binaries (macOS)'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] Signed macOS binaries invalidate the bundled checksums.json

prepare-runtime.js writes checksums.json at the end of Prepare bundled runtime (line 314) and smoke:runtime verifies it immediately after. This step then calls codesign --force on node/bin/node and the rg binaries, changing their SHA-256. The published macOS app bundle therefore ships an integrity manifest whose listed hashes no longer match the actual signed binaries.

Suggested fix: regenerate checksums.json after the codesign calls (extract the checksum-writing logic into a reusable script), or move the signing calls before writeChecksums() inside prepare-runtime.js when APPLE_SIGNING_IDENTITY is available.

if: "runner.os == 'macOS' && inputs.dry_run == false"
working-directory: 'packages/desktop-shell'
shell: 'bash'
env:
APPLE_SIGNING_IDENTITY: '${{ env.APPLE_SIGNING_IDENTITY }}'
run: |
set -euo pipefail
# Sign all native macOS executables in the bundled runtime so
# notarization does not reject them. Tauri only signs the main
# app binary; resources like ripgrep and the Node.js runtime are
# embedded verbatim and must be signed beforehand.
runtime_dir="runtime/qwen-code"
# ripgrep vendor binaries
rg_dir="$runtime_dir/lib/vendor/ripgrep"
if [ -d "$rg_dir" ]; then
find "$rg_dir" -type f -name 'rg' -exec \
codesign --force --sign "$APPLE_SIGNING_IDENTITY" \
--options runtime --timestamp \
--entitlements src-tauri/Entitlements.plist {} \;
fi
# Node.js runtime binary
node_bin="$runtime_dir/node/bin/node"
if [ -f "$node_bin" ]; then
codesign --force --sign "$APPLE_SIGNING_IDENTITY" \
--options runtime --timestamp \
--entitlements src-tauri/Entitlements.plist "$node_bin"
fi

- name: 'Build desktop installers'
working-directory: 'packages/desktop-shell'
shell: 'bash'
Expand Down
Loading