hi-client/scripts/sign_and_notarize.sh

305 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
set -e
# ╔═══════════════════════════════════════════════════════════════════════════╗
# ║ HiFastVPN macOS 签名和公证脚本 ║
# ║ ║
# ║ 使用方法: ║
# ║ 1. 修改下面的配置变量 ║
# ║ 2. chmod +x sign_and_notarize.sh ║
# ║ 3. ./sign_and_notarize.sh ║
# ╚═══════════════════════════════════════════════════════════════════════════╝
# ======================== 配置变量(请修改这里)========================
SIGNING_IDENTITY="Developer ID Application: TAW TRADERS SDN. BHD. (NJRRF427XB)"
APPLE_ID="speakeloudest@gmail.com"
APP_PASSWORD="lvry-umfn-pqgz-lnwk" # App-specific password
TEAM_ID="NJRRF427XB"
# 路径配置
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
APP_PATH="${PROJECT_DIR}/build/macos/Build/Products/Release/HiFastVPN.app"
ENTITLEMENTS="${PROJECT_DIR}/macos/Runner/Release.entitlements"
OUTPUT_DIR="${PROJECT_DIR}/dist"
BACKGROUND_IMAGE="${SCRIPT_DIR}/assets/dmg_bg.png"
# =====================================================================
echo "╔═══════════════════════════════════════════════════════════════════╗"
echo "║ HiFastVPN macOS 签名和公证脚本 ║"
echo "╚═══════════════════════════════════════════════════════════════════╝"
echo ""
# 提取版本号
PUBSPEC_PATH="${PROJECT_DIR}/pubspec.yaml"
if [ ! -f "${PUBSPEC_PATH}" ]; then
echo "❌ 错误: 找不到 pubspec.yaml"
exit 1
fi
VERSION=$(grep "^version:" "${PUBSPEC_PATH}" | sed 's/version: *//' | tr -d ' ')
if [ -z "${VERSION}" ]; then
echo "❌ 错误: 无法提取版本号"
exit 1
fi
echo "📦 版本号: ${VERSION}"
echo "📁 项目目录: ${PROJECT_DIR}"
echo "📦 应用路径: ${APP_PATH}"
echo "📄 Entitlements: ${ENTITLEMENTS}"
echo "🖼️ 背景图: ${BACKGROUND_IMAGE}"
echo ""
# 检查 .app 是否存在
if [ ! -d "${APP_PATH}" ]; then
echo "❌ 错误: ${APP_PATH} 不存在"
echo "请先运行: flutter build macos --release"
exit 1
fi
# 检查 entitlements 是否存在
if [ ! -f "${ENTITLEMENTS}" ]; then
echo "❌ 错误: ${ENTITLEMENTS} 不存在"
exit 1
fi
# 检查 create-dmg 是否安装
if ! command -v create-dmg >/dev/null 2>&1; then
echo "❌ 错误: create-dmg 未安装brew install create-dmg"
exit 1
fi
# 检查背景图是否存在
if [ ! -f "${BACKGROUND_IMAGE}" ]; then
echo "❌ 错误: 背景图不存在"
exit 1
fi
# 校验背景图尺寸
BG_INFO=$(sips -g pixelWidth -g pixelHeight "${BACKGROUND_IMAGE}")
BG_W=$(echo "$BG_INFO" | awk '/pixelWidth/ {print $2}')
BG_H=$(echo "$BG_INFO" | awk '/pixelHeight/ {print $2}')
if [ "$BG_W" != "1200" ] || [ "$BG_H" != "800" ]; then
echo "❌ 背景图必须是 1200x800当前是 ${BG_W}x${BG_H}"
exit 1
fi
# 创建输出目录
mkdir -p "${OUTPUT_DIR}"
# ======================== Step 1: 清理 ========================
echo ""
echo "🧹 Step 1: 清理多余文件..."
# 删除 .DS_Store
find "${APP_PATH}" -name ".DS_Store" -delete 2>/dev/null || true
find "${APP_PATH}" -name "*.bak" -delete 2>/dev/null || true
# 删除 Headers 目录和符号链接
find "${APP_PATH}/Contents/Frameworks" -type l -name "Headers" -delete 2>/dev/null || true
find "${APP_PATH}/Contents/Frameworks" -type d -name "Headers" -exec rm -rf {} + 2>/dev/null || true
# 删除 Modules 目录
find "${APP_PATH}/Contents/Frameworks" -type d -name "Modules" -exec rm -rf {} + 2>/dev/null || true
# 删除 PrivateHeaders
find "${APP_PATH}/Contents/Frameworks" -name "PrivateHeaders" -exec rm -rf {} + 2>/dev/null || true
echo " ✅ 清理完成"
# ======================== Step 2: 移除现有签名 ========================
echo ""
echo "🗑️ Step 2: 移除现有签名..."
# 移除所有签名(忽略错误)
find "${APP_PATH}" -name "*.dylib" -exec codesign --remove-signature {} \; 2>/dev/null || true
find "${APP_PATH}" -name "*.framework" -exec codesign --remove-signature {} \; 2>/dev/null || true
codesign --remove-signature "${APP_PATH}" 2>/dev/null || true
echo " ✅ 移除签名完成"
# ======================== Step 3: 签名 dylib 文件 ========================
echo ""
echo "🔐 Step 3: 签名 dylib 文件..."
find "${APP_PATH}" -name "*.dylib" -print0 | while IFS= read -r -d '' dylib; do
echo "$(basename "$dylib")"
codesign --force --sign "${SIGNING_IDENTITY}" \
--options runtime \
--timestamp \
"$dylib"
done
echo " ✅ dylib 签名完成"
# ======================== Step 4: 签名 Framework ========================
echo ""
echo "🔐 Step 4: 签名 Framework..."
# 签名所有 framework使用 --deep 确保内部资源被正确处理)
for fw in "${APP_PATH}"/Contents/Frameworks/*.framework; do
if [ -d "$fw" ]; then
fw_name=$(basename "$fw")
echo "${fw_name}"
# 使用 --deep 签名 framework这样会自动处理内部的资源
codesign --force --sign "${SIGNING_IDENTITY}" \
--options runtime \
--timestamp \
"$fw"
fi
done
echo " ✅ Framework 签名完成"
# ======================== Step 5: 签名主应用 ========================
echo ""
echo "🔐 Step 5: 签名主应用..."
codesign --force --sign "${SIGNING_IDENTITY}" \
--options runtime \
--entitlements "${ENTITLEMENTS}" \
--timestamp \
"${APP_PATH}"
echo " ✅ 主应用签名完成"
# ======================== Step 6: 验证签名 ========================
echo ""
echo "✅ Step 6: 验证签名..."
if codesign --verify --deep --strict --verbose=2 "${APP_PATH}" 2>&1; then
echo " ✅ 签名验证通过!"
else
echo " ❌ 签名验证失败!"
echo ""
echo "详细错误信息:"
codesign --verify --deep --strict --verbose=4 "${APP_PATH}" 2>&1
exit 1
fi
# ======================== Step 7: 创建 ZIP ========================
echo ""
echo "📦 Step 7: 创建 ZIP..."
ZIP_PATH="${OUTPUT_DIR}/HiFastVPN.zip"
rm -f "${ZIP_PATH}"
ditto -c -k --keepParent "${APP_PATH}" "${ZIP_PATH}"
echo " ✅ ZIP 创建完成: ${ZIP_PATH}"
# ======================== Step 8: 公证 ========================
echo ""
echo "📤 Step 8: 提交公证..."
echo " (这可能需要几分钟时间...)"
xcrun notarytool submit "${ZIP_PATH}" \
--apple-id "${APPLE_ID}" \
--password "${APP_PASSWORD}" \
--team-id "${TEAM_ID}" \
--wait
echo " ✅ 公证完成"
# ======================== Step 9: Staple ========================
echo ""
echo "📎 Step 9: Staple..."
xcrun stapler staple "${APP_PATH}"
echo " ✅ Staple 完成"
# ======================== Step 10: 最终验证 ========================
echo ""
echo "🔍 Step 10: 最终验证..."
echo ""
echo "--- codesign --verify ---"
codesign --verify --deep --strict "${APP_PATH}" && echo "✅ codesign 验证通过"
echo ""
echo "--- xcrun stapler validate ---"
xcrun stapler validate "${APP_PATH}"
echo ""
echo "--- spctl -a -t execute ---"
spctl -a -t execute -vv "${APP_PATH}"
# ======================== Step 11: 创建 DMG ========================
echo ""
echo "💿 Step 11: 创建 DMG..."
DMG_NAME="HiFastVPN-${VERSION}.dmg"
DMG_PATH="${OUTPUT_DIR}/${DMG_NAME}"
rm -f "${DMG_PATH}"
# 创建临时目录并复制 .app
TEMP_DIR=$(mktemp -d)
cp -R "${APP_PATH}" "${TEMP_DIR}/"
# Finder 坐标计算(左上原点,中心点对齐)
# 背景1200 × 800
# HiFastVPN.app 中心:左 228, 上 418, 尺寸 168×168
APP_CENTER_X=236
APP_CENTER_Y=428
APP_ICON_SIZE=152
APP_X=$((APP_CENTER_X + APP_ICON_SIZE / 2))
APP_Y=$((APP_CENTER_Y + APP_ICON_SIZE / 2))
# Applications 图标中心:左 742, 上 372, 尺寸 259×259
DROP_CENTER_X=702
DROP_CENTER_Y=372
DROP_ICON_SIZE=259
DROP_X=$((DROP_CENTER_X + DROP_ICON_SIZE / 2))
DROP_Y=$((DROP_CENTER_Y + DROP_ICON_SIZE / 2))
# 使用 create-dmg 创建自定义 DMG
echo " 创建自定义 DMG..."
create-dmg \
--volname "HiFastVPN Installation" \
--background "${BACKGROUND_IMAGE}" \
--window-size 1200 800 \
--icon-size "${APP_ICON_SIZE}" \
--icon "HiFastVPN.app" "${APP_X}" "${APP_Y}" \
--app-drop-link "${DROP_X}" "${DROP_Y}" \
--hide-extension "HiFastVPN.app" \
--no-internet-enable \
"${DMG_PATH}" \
"${TEMP_DIR}"
# 清理临时目录
rm -rf "${TEMP_DIR}"
# 公证 DMG
echo " 公证 DMG..."
xcrun notarytool submit "${DMG_PATH}" \
--apple-id "${APPLE_ID}" \
--password "${APP_PASSWORD}" \
--team-id "${TEAM_ID}" \
--wait
# Staple DMG
echo " Staple DMG..."
xcrun stapler staple "${DMG_PATH}"
echo " ✅ DMG 创建完成: ${DMG_PATH}"
# ======================== 完成 ========================
echo ""
echo "╔═══════════════════════════════════════════════════════════════════╗"
echo "║ 🎉 全部完成! ║"
echo "╚═══════════════════════════════════════════════════════════════════╝"
echo ""
echo "输出文件:"
echo " 📦 ${APP_PATH}"
echo " 📦 ${ZIP_PATH}"
echo " 💿 ${DMG_PATH}"
echo ""
echo "⚠️ 重要提示:"
echo " 发给用户的应该是 DMG 文件,不是 ZIP 或 .app"
echo " DMG 已经签名和公证,用户打开后不会被 Gatekeeper 拦截"
echo " 图标和 Applications 已按设计稿中心点对齐"
echo ""