From d2db9b7054a0d0e47494b7f35a40ac4fa775a3f7 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: Mon, 27 Apr 2026 21:30:35 +0800 Subject: [PATCH] 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 --- desktop/scripts/build-macos-arm64.sh | 45 ++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/desktop/scripts/build-macos-arm64.sh b/desktop/scripts/build-macos-arm64.sh index e5e1998a..407d7710 100755 --- a/desktop/scripts/build-macos-arm64.sh +++ b/desktop/scripts/build-macos-arm64.sh @@ -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 ` 来统一 # 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}" \