[fix] preserve macOS permissions and defer legacy migration to launch

This commit is contained in:
dijunkun
2026-11-07 03:07:24 +08:00
parent 458f9ef58d
commit f5f8eb94d4
17 changed files with 613 additions and 203 deletions
+10 -6
View File
@@ -70,7 +70,7 @@ DMG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-${APP_VERSION}.dmg"
VOL_NAME="Install ${APP_NAME_UPPER}"
echo "delete old files"
rm -rf "${APP_BUNDLE}" "${PKG_NAME}" "${DMG_NAME}" build_pkg_temp CrossDesk_dmg_temp
rm -rf "${APP_BUNDLE}" "${PKG_NAME}" "${DMG_NAME}" build_pkg_temp build_pkg_scripts CrossDesk_dmg_temp
mkdir -p build_pkg_temp
mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
@@ -132,9 +132,13 @@ find "${APP_BUNDLE}" -name '._*' -delete
echo ".app created successfully."
mkdir -p build_pkg_scripts
cp "${SCRIPT_DIR}/tcc_postinstall.sh" build_pkg_scripts/postinstall
chmod +x build_pkg_scripts/postinstall
PKG_SCRIPT_ARGS=()
if [[ "${CROSSDESK_MACOS_SIGN_IDENTITY:--}" != "-" ]]; then
mkdir -p build_pkg_scripts
cp "${SCRIPT_DIR}/tcc_migration_preinstall.sh" build_pkg_scripts/preinstall
chmod +x build_pkg_scripts/preinstall
PKG_SCRIPT_ARGS=(--scripts build_pkg_scripts)
fi
mkdir -p build_pkg_resources
cat > build_pkg_resources/welcome.html <<EOF
@@ -165,7 +169,7 @@ cat > build_pkg_resources/welcome.html <<EOF
<h1>欢迎安装 ${INSTALLER_TITLE}</h1>
<p>CrossDesk 将安装到“应用程序”文件夹。</p>
<p>首次启动时,CrossDesk 会引导你在系统设置中授予必要权限,包括辅助功能、录屏与系统录音等。</p>
<p>为避免旧版本授权残留造成状态误判,安装后可能需要重新授权。</p>
<p>正式签名版本正常升级时会保留已有权限。从未签名或 ad-hoc 旧版升级后,请退出旧版并启动新版。新版会迁移当前用户的录屏与辅助功能授权,并引导你重新授权。遇到异常可在“设置 → 系统权限”中重试或稍后处理。</p>
<p>安装完成后,请从“应用程序”文件夹启动 CrossDesk。</p>
</body>
</html>
@@ -177,7 +181,7 @@ pkgbuild \
--version "${APP_VERSION}" \
--install-location "/Applications" \
--component "${APP_BUNDLE}" \
--scripts build_pkg_scripts \
${PKG_SCRIPT_ARGS[@]+"${PKG_SCRIPT_ARGS[@]}"} \
build_pkg_temp/${APP_NAME}-component.pkg
cat > build_pkg_temp/Distribution <<EOF
+10 -6
View File
@@ -70,7 +70,7 @@ DMG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-${APP_VERSION}.dmg"
VOL_NAME="Install ${APP_NAME_UPPER}"
echo "delete old files"
rm -rf "${APP_BUNDLE}" "${PKG_NAME}" "${DMG_NAME}" build_pkg_temp CrossDesk_dmg_temp
rm -rf "${APP_BUNDLE}" "${PKG_NAME}" "${DMG_NAME}" build_pkg_temp build_pkg_scripts CrossDesk_dmg_temp
mkdir -p build_pkg_temp
mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
@@ -132,9 +132,13 @@ find "${APP_BUNDLE}" -name '._*' -delete
echo ".app created successfully."
mkdir -p build_pkg_scripts
cp "${SCRIPT_DIR}/tcc_postinstall.sh" build_pkg_scripts/postinstall
chmod +x build_pkg_scripts/postinstall
PKG_SCRIPT_ARGS=()
if [[ "${CROSSDESK_MACOS_SIGN_IDENTITY:--}" != "-" ]]; then
mkdir -p build_pkg_scripts
cp "${SCRIPT_DIR}/tcc_migration_preinstall.sh" build_pkg_scripts/preinstall
chmod +x build_pkg_scripts/preinstall
PKG_SCRIPT_ARGS=(--scripts build_pkg_scripts)
fi
mkdir -p build_pkg_resources
cat > build_pkg_resources/welcome.html <<EOF
@@ -165,7 +169,7 @@ cat > build_pkg_resources/welcome.html <<EOF
<h1>欢迎安装 ${INSTALLER_TITLE}</h1>
<p>CrossDesk 将安装到“应用程序”文件夹。</p>
<p>首次启动时,CrossDesk 会引导你在系统设置中授予必要权限,包括辅助功能、录屏与系统录音等。</p>
<p>为避免旧版本授权残留造成状态误判,安装后可能需要重新授权。</p>
<p>正式签名版本正常升级时会保留已有权限。从未签名或 ad-hoc 旧版升级后,请退出旧版并启动新版。新版会迁移当前用户的录屏与辅助功能授权,并引导你重新授权。遇到异常可在“设置 → 系统权限”中重试或稍后处理。</p>
<p>安装完成后,请从“应用程序”文件夹启动 CrossDesk。</p>
</body>
</html>
@@ -177,7 +181,7 @@ pkgbuild \
--version "${APP_VERSION}" \
--install-location "/Applications" \
--component "${APP_BUNDLE}" \
--scripts build_pkg_scripts \
${PKG_SCRIPT_ARGS[@]+"${PKG_SCRIPT_ARGS[@]}"} \
build_pkg_temp/${APP_NAME}-component.pkg
cat > build_pkg_temp/Distribution <<EOF
@@ -0,0 +1,63 @@
#!/bin/bash
set -u
export LC_ALL=C
# Migration is best effort. Installing the application must remain possible
# even when its old privacy records or the migration directory are unavailable.
if [[ $# -lt 3 ]]; then
echo "CrossDesk permission migration skipped because installer arguments are missing." >&2
exit 0
fi
APP_BUNDLE="${3%/}${2%/}/CrossDesk.app"
if [[ ! -d "$APP_BUNDLE" ]]; then
exit 0
fi
BUNDLE_ID="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' \
"$APP_BUNDLE/Contents/Info.plist" 2>/dev/null || true)"
case "$BUNDLE_ID" in
cn.crossdesk.app|cn.crossdesk.CrossDesk|com.crossdesk.app|com.crossdesk.CrossDesk|com.kunkundi.crossdesk|com.kunkundi.CrossDesk)
;;
*) exit 0 ;;
esac
if SIGNATURE="$(/usr/bin/codesign --display --verbose=4 "$APP_BUNDLE" 2>&1)"; then
if ! /usr/bin/grep -Fxq 'Signature=adhoc' <<< "$SIGNATURE"; then
# Keep any unfinished migration for users who have not launched yet.
exit 0
fi
elif [[ "$SIGNATURE" != *": code object is not signed at all" ]]; then
echo "Could not identify the old CrossDesk signature. Preserving migration state." >&2
exit 0
fi
STATE_DIR="${3%/}/Library/Application Support/CrossDesk"
if [[ "$EUID" -ne 0 || -L "$STATE_DIR" ]]; then
echo "Could not safely record the CrossDesk permission migration." >&2
exit 0
fi
if ! /bin/mkdir -p "$STATE_DIR" ||
[[ "$(/usr/bin/stat -f '%u' "$STATE_DIR")" != 0 ]] ||
! /bin/chmod 755 "$STATE_DIR"; then
echo "CrossDesk migration directory is unavailable. Use permission repair after installation." >&2
exit 0
fi
TEMP_FILE="$(/usr/bin/mktemp "$STATE_DIR/.permission-migration.XXXXXX")" || exit 0
trap '/bin/rm -f "$TEMP_FILE"' EXIT
MIGRATION_ID="$(/usr/bin/uuidgen)" || exit 0
if /usr/bin/plutil -create xml1 "$TEMP_FILE" &&
/usr/bin/plutil -insert schema -integer 1 "$TEMP_FILE" &&
/usr/bin/plutil -insert migration_id -string "$MIGRATION_ID" "$TEMP_FILE" &&
/usr/bin/plutil -insert legacy_bundle_id -string "$BUNDLE_ID" "$TEMP_FILE" &&
/usr/bin/plutil -insert app_path -string "${2%/}/CrossDesk.app" "$TEMP_FILE" &&
/usr/bin/plutil -convert json "$TEMP_FILE" &&
/bin/chmod 644 "$TEMP_FILE" &&
/bin/mv -f "$TEMP_FILE" "$STATE_DIR/permission-migration.json"; then
echo "Recorded CrossDesk permission migration. Quit the old app and launch the new version to continue."
else
echo "Could not record CrossDesk permission migration. Use permission repair after installation." >&2
fi
exit 0
@@ -1,112 +0,0 @@
#!/bin/bash
set -e
APP_IDENTIFIER="cn.crossdesk.app"
# Keep known historical identifiers here. tccutil only resets identifiers that
# Launch Services can currently resolve, so path/db cleanup below remains a
# best-effort fallback for stale entries from unsigned or removed builds.
BUNDLE_IDENTIFIERS=(
"cn.crossdesk.app"
"cn.crossdesk.CrossDesk"
"com.crossdesk.app"
"com.crossdesk.CrossDesk"
"com.kunkundi.crossdesk"
"com.kunkundi.CrossDesk"
)
TCC_SERVICES=(
"ScreenCapture"
"Accessibility"
"Microphone"
"AudioCapture"
)
run_tccutil() {
local user_name="$1"
local user_id="$2"
local service="$3"
local bundle_id="$4"
if [ -n "$user_name" ] && [ -n "$user_id" ]; then
/bin/launchctl asuser "$user_id" \
/usr/bin/sudo -u "$user_name" \
/usr/bin/tccutil reset "$service" "$bundle_id" >/dev/null 2>&1
else
/usr/bin/tccutil reset "$service" "$bundle_id" >/dev/null 2>&1
fi
}
reset_bundle_tcc() {
local user_name="$1"
local user_id="$2"
local bundle_id
local service
for bundle_id in "${BUNDLE_IDENTIFIERS[@]}"; do
if run_tccutil "$user_name" "$user_id" "All" "$bundle_id"; then
continue
fi
for service in "${TCC_SERVICES[@]}"; do
run_tccutil "$user_name" "$user_id" "$service" "$bundle_id" || true
done
done
}
cleanup_tcc_db() {
local db_path="$1"
if [ ! -f "$db_path" ] || ! command -v sqlite3 >/dev/null 2>&1; then
return
fi
/usr/bin/sqlite3 "$db_path" <<'SQL' >/dev/null 2>&1 || true
DELETE FROM access
WHERE service IN (
'kTCCServiceScreenCapture',
'kTCCServiceAccessibility',
'kTCCServiceMicrophone',
'kTCCServiceAudioCapture'
)
AND (
client IN (
'cn.crossdesk.app',
'cn.crossdesk.CrossDesk',
'com.crossdesk.app',
'com.crossdesk.CrossDesk',
'com.kunkundi.crossdesk',
'com.kunkundi.CrossDesk'
)
OR lower(client) LIKE '%crossdesk%'
);
SQL
}
cleanup_user_tcc_db() {
local user_name="$1"
local home_dir
home_dir=$(/usr/bin/dscl . -read "/Users/${user_name}" NFSHomeDirectory 2>/dev/null |
/usr/bin/awk '{print $2}')
if [ -z "$home_dir" ]; then
return
fi
cleanup_tcc_db "${home_dir}/Library/Application Support/com.apple.TCC/TCC.db"
}
CONSOLE_USER=$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null || true)
if [ -n "$CONSOLE_USER" ] &&
[ "$CONSOLE_USER" != "root" ] &&
[ "$CONSOLE_USER" != "loginwindow" ]; then
CONSOLE_UID=$(/usr/bin/id -u "$CONSOLE_USER" 2>/dev/null || true)
reset_bundle_tcc "$CONSOLE_USER" "$CONSOLE_UID"
cleanup_user_tcc_db "$CONSOLE_USER"
fi
# Also clear any system/root-scoped decisions as a harmless fallback.
reset_bundle_tcc "" ""
cleanup_tcc_db "/Library/Application Support/com.apple.TCC/TCC.db"
exit 0