Keep local macOS test packages launchable without changing release CI

The local Apple Silicon packaging script produced app bundles that could
fail strict bundle validation because the outer .app had no sealed
resources. The fix shallow-signs only the copied canonical app bundle and
checks that the claude-sidecar code-signature hash stays unchanged.

Constraint: GitHub Actions release packaging must remain on the existing tauri-action path
Constraint: macOS Keychain ACLs are sensitive to sidecar code-signature hashes
Rejected: Set macOS signingIdentity in release CI | Tauri re-signs nested sidecars and can change Keychain caller identity
Confidence: high
Scope-risk: narrow
Directive: Do not deep-sign claude-sidecar without proving existing Keychain ACLs still work after upgrade
Tested: bash -n desktop/scripts/build-macos-arm64.sh
Tested: SKIP_INSTALL=1 desktop/scripts/build-macos-arm64.sh
Tested: codesign --verify --deep --strict --verbose=2 desktop/build-artifacts/macos-arm64/Claude\ Code\ Haha.app
Tested: open -n desktop/build-artifacts/macos-arm64/Claude\ Code\ Haha.app and /health returned ok
Not-tested: GitHub Actions release workflow, intentionally unchanged
This commit is contained in:
程序员阿江(Relakkes) 2026-04-27 21:30:35 +08:00
parent 438c9efbbc
commit d2db9b7054

View File

@ -78,6 +78,11 @@ rm -rf "${DESKTOP_DIR}/src-tauri/binaries/claude-sidecar-"*
rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/bundle"
rm -rf "${DESKTOP_DIR}/src-tauri/target/release/bundle"
rm -rf "${DESKTOP_DIR}/dist"
rm -f "${DESKTOP_DIR}/tsconfig.tsbuildinfo"
rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/build/claude-code-desktop-"*
rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/.fingerprint/claude-code-desktop-"*
rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/claude_code_desktop-"*
rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/libclaude_code_desktop-"*
echo "[build-macos-arm64] Rebuilding frontend (tsc + vite)..."
(cd "${DESKTOP_DIR}" && bun run build)
@ -217,19 +222,55 @@ APPLESCRIPT
rm -f "${rw_dmg}"
}
codesign_cdhash() {
local executable="$1"
codesign -d --verbose=4 "${executable}" 2>&1 \
| awk -F= '/^CDHash=/{print $2; exit}'
}
sign_canonical_app_bundle() {
local app_bundle="$1"
local sidecar="${app_bundle}/Contents/MacOS/claude-sidecar"
local sidecar_cdhash_before=""
local sidecar_cdhash_after=""
if [[ -x "${sidecar}" ]]; then
sidecar_cdhash_before="$(codesign_cdhash "${sidecar}")"
fi
# Tauri --no-sign leaves the outer .app with no sealed resources, which
# fails strict bundle validation once Resources/icon.icns exists. Sign only
# the outer bundle: do not pass --deep, because re-signing claude-sidecar
# changes its code-signature hash and breaks existing macOS Keychain ACLs.
codesign --force --sign - --timestamp=none "${app_bundle}"
if [[ -x "${sidecar}" ]]; then
sidecar_cdhash_after="$(codesign_cdhash "${sidecar}")"
if [[ "${sidecar_cdhash_before}" != "${sidecar_cdhash_after}" ]]; then
echo "[build-macos-arm64] ERROR: sidecar signature hash changed while signing app bundle" >&2
echo "[build-macos-arm64] before=${sidecar_cdhash_before}" >&2
echo "[build-macos-arm64] after=${sidecar_cdhash_after}" >&2
exit 1
fi
fi
codesign --verify --deep --strict --verbose=2 "${app_bundle}"
}
if [[ -n "${LATEST_DMG}" ]]; then
cp -f "${LATEST_DMG}" "${CANONICAL_OUTPUT_DIR}/"
fi
if [[ -n "${LATEST_APP}" ]]; then
# 注意: 不要再对 .app 重签名。曾经脚本在这里跑过
# 不要 deep re-sign。曾经脚本在这里跑过
# `codesign --force --deep --sign - --identifier <bundle-id>` 来统一
# sidecar 和外层的 signing identifier,但这会改变 sidecar binary 的
# code signature hash —— macOS Keychain ACL 按 hash 识别 caller,
# 重签完再访问时会被 ACL 当作"陌生 binary"静默拒绝,导致 CLI 读不到
# OAuth token,最终请求打到 Anthropic 返回 403 "Request not allowed"。
# Tauri 的 --no-sign 其实已经做了 ad-hoc 签名,直接用即可。
# 这里只浅签外层 bundle,让 .app 拥有有效资源封印,同时保留 sidecar hash
cp -R "${LATEST_APP}" "${CANONICAL_OUTPUT_DIR}/"
sign_canonical_app_bundle "${CANONICAL_OUTPUT_DIR}/${APP_BUNDLE_NAME}"
rm -f "${CANONICAL_OUTPUT_DIR}/"*.dmg
build_canonical_dmg \
"${CANONICAL_OUTPUT_DIR}/${APP_BUNDLE_NAME}" \