chore(desktop-client): add macOS code-signing and notarization scaffolding
- Move the build config out of package.json into electron-builder.yml so
the mac block can carry hardenedRuntime, entitlements, universal arch,
and usage-description Info.plist keys. Drop identity:null (which
forces unsigned builds) and let CSC_NAME or the keychain decide.
- Add entitlements plist files and three helper scripts: sign-setup.sh
(one-time keychain prep with notarytool store-credentials),
sign-mac.sh (sign + notarize + staple, supports keychain or CI env
vars), and ci-import-cert.sh (CI keychain bootstrap).
- Ignore .env.signing so local credentials don't sneak into the repo,
and pin @emnapi/{core,runtime} as devDependencies so universal builds
resolve them deterministically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ coverage/
|
||||
dist/
|
||||
.env
|
||||
.env.local
|
||||
.env.signing
|
||||
apps/*/.env.signing
|
||||
apps/*/dist/
|
||||
apps/*/out/
|
||||
apps/*/release/
|
||||
|
||||
@@ -365,3 +365,5 @@ The color system is almost entirely achromatic — dark backgrounds with white/g
|
||||
5. Brand indigo (`#5e6ad2` / `#7170ff`) is the only chromatic color — everything else is grayscale
|
||||
6. Borders are always semi-transparent white, never solid dark colors on dark backgrounds
|
||||
7. Berkeley Mono for any code or technical content, Inter Variable for everything else
|
||||
|
||||
rnzy-benw-ynai-ftbl
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key><true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key><true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key><true/>
|
||||
<key>com.apple.security.inherit</key><true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key><true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key><true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key><true/>
|
||||
<key>com.apple.security.device.audio-input</key><true/>
|
||||
<key>com.apple.security.device.camera</key><true/>
|
||||
<key>com.apple.security.network.client</key><true/>
|
||||
<key>com.apple.security.network.server</key><true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key><true/>
|
||||
<key>com.apple.security.automation.apple-events</key><true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -35,18 +35,25 @@ files:
|
||||
- "!**/node_modules/**/*.{c,cc,cpp,h,hpp,cmake,m4,am,sh}"
|
||||
mac:
|
||||
category: public.app-category.productivity
|
||||
identity: null
|
||||
notarize: false
|
||||
hardenedRuntime: false
|
||||
# identity 不写死:让 electron-builder 自动从 CSC_NAME 环境变量或钥匙串里挑
|
||||
# 注意:写 `identity: null` 是"明确不签名",不要这么写
|
||||
notarize: false # 我们用 notarytool 自己跑,比 electron-builder 内置更稳
|
||||
hardenedRuntime: true
|
||||
gatekeeperAssess: false
|
||||
entitlements: build/entitlements.mac.plist
|
||||
entitlementsInherit: build/entitlements.mac.inherit.plist
|
||||
electronLanguages:
|
||||
- en
|
||||
- zh_CN
|
||||
extendInfo:
|
||||
NSCameraUsageDescription: 用于视频会议和录屏功能
|
||||
NSMicrophoneUsageDescription: 用于音频采集
|
||||
NSAppleEventsUsageDescription: 用于自动化控制其他应用
|
||||
target:
|
||||
- target: dmg
|
||||
arch: [arm64]
|
||||
- target: zip
|
||||
arch: [arm64]
|
||||
arch: [arm64, universal]
|
||||
# - target: zip
|
||||
# arch: [arm64, universal]
|
||||
dmg:
|
||||
format: ULFO
|
||||
contents:
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
"dev": "unset ELECTRON_RUN_AS_NODE && electron-vite dev --inspect=9229",
|
||||
"build": "electron-vite build",
|
||||
"package:mac": "electron-builder --mac --arm64",
|
||||
"sign:setup": "bash scripts/sign-setup.sh",
|
||||
"sign:mac": "bash scripts/sign-mac.sh",
|
||||
"test": "vitest run",
|
||||
"typecheck": "vue-tsc --noEmit"
|
||||
},
|
||||
@@ -46,81 +48,5 @@
|
||||
"vue": "^3.5.31",
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-tsc": "^3.2.6"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.geo.rankly.desktop",
|
||||
"productName": "省心推",
|
||||
"copyright": "Copyright © 2026 shengxintui.com. All Rights Reserved.",
|
||||
"directories": {
|
||||
"output": "release",
|
||||
"buildResources": "build"
|
||||
},
|
||||
"compression": "maximum",
|
||||
"removePackageScripts": true,
|
||||
"removePackageKeywords": true,
|
||||
"asar": true,
|
||||
"asarUnpack": [
|
||||
"**/node_modules/better-sqlite3/build/Release/**",
|
||||
"**/node_modules/better-sqlite3/lib/**",
|
||||
"**/node_modules/bindings/**",
|
||||
"**/node_modules/file-uri-to-path/**",
|
||||
"**/node_modules/playwright-core/lib/**",
|
||||
"**/node_modules/playwright-core/bin/**"
|
||||
],
|
||||
"files": [
|
||||
"out/**/*",
|
||||
"package.json",
|
||||
"!**/*.map",
|
||||
"!**/*.ts",
|
||||
"!**/*.tsx",
|
||||
"!**/*.md",
|
||||
"!**/*.markdown",
|
||||
"!**/{test,tests,__tests__,__mocks__,example,examples,docs,doc,demo,samples,man}/**",
|
||||
"!**/{LICENSE,LICENSE.*,license,CHANGELOG,CHANGELOG.*,README,README.*,HISTORY,HISTORY.*,AUTHORS,CONTRIBUTING}*",
|
||||
"!**/.*",
|
||||
"!**/node_modules/.bin/**",
|
||||
"!**/node_modules/*/{Makefile,Gulpfile.js,Gruntfile.js,CMakeLists.txt,*.coffee,*.flow,*.c,*.cc,*.cpp,*.h,*.hpp,*.gyp,*.gypi,*.tgz,binding.gyp}",
|
||||
"!**/node_modules/**/{*.d.ts,tsconfig*.json,jest.config*,rollup.config*,webpack.config*,vite.config*}",
|
||||
"!**/node_modules/better-sqlite3/{deps,src,docs,benchmark}/**",
|
||||
"!**/node_modules/better-sqlite3/{test_extension.c,download.sh,*.gyp,*.gypi}",
|
||||
"!**/node_modules/playwright-core/{types,ThirdPartyNotices.txt}/**",
|
||||
"!**/node_modules/**/*.{c,cc,cpp,h,hpp,cmake,m4,am,sh}"
|
||||
],
|
||||
"mac": {
|
||||
"category": "public.app-category.productivity",
|
||||
"identity": null,
|
||||
"electronLanguages": ["en", "zh_CN"],
|
||||
"target": [
|
||||
{ "target": "dmg", "arch": ["arm64"] },
|
||||
{ "target": "zip", "arch": ["arm64"] }
|
||||
],
|
||||
"notarize": false,
|
||||
"hardenedRuntime": false,
|
||||
"gatekeeperAssess": false
|
||||
},
|
||||
"dmg": {
|
||||
"format": "ULFO",
|
||||
"contents": [
|
||||
{ "x": 130, "y": 220 },
|
||||
{ "x": 410, "y": 220, "type": "link", "path": "/Applications" }
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
{ "target": "nsis", "arch": ["x64"] }
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"perMachine": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
{ "target": "AppImage", "arch": ["x64"] }
|
||||
],
|
||||
"category": "Office"
|
||||
},
|
||||
"publish": null
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
# CI 专用:把 base64 编码的 .p12 证书导入到一个临时钥匙串
|
||||
# 仅在 CI 环境调用,本地不要跑(会污染你的登录钥匙串体验)
|
||||
#
|
||||
# 必需环境变量:
|
||||
# CSC_LINK - base64 编码的 .p12 文件内容
|
||||
# CSC_KEY_PASSWORD - .p12 文件的密码
|
||||
# KEYCHAIN_PASSWORD - 临时钥匙串自身的密码(任意字符串,例如 secrets.GITHUB_TOKEN 截断都行)
|
||||
#
|
||||
# 副作用:创建 ~/Library/Keychains/build.keychain-db,已加入搜索路径并解锁
|
||||
# 销毁:脚本注册了 trap,进程退出时自动 delete-keychain
|
||||
set -euo pipefail
|
||||
|
||||
: "${CSC_LINK:?CI 必须提供 CSC_LINK (base64 .p12)}"
|
||||
: "${CSC_KEY_PASSWORD:?CI 必须提供 CSC_KEY_PASSWORD}"
|
||||
: "${KEYCHAIN_PASSWORD:?CI 必须提供 KEYCHAIN_PASSWORD(用于临时钥匙串自身)}"
|
||||
|
||||
KEYCHAIN_NAME="build.keychain"
|
||||
CERT_PATH="$RUNNER_TEMP/build_certificate.p12"
|
||||
[ -z "${RUNNER_TEMP:-}" ] && CERT_PATH="/tmp/build_certificate.p12"
|
||||
|
||||
# 1. 解码证书到临时文件
|
||||
echo "$CSC_LINK" | base64 --decode > "$CERT_PATH"
|
||||
|
||||
# 2. 创建临时钥匙串
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
||||
security set-keychain-settings -lut 21600 "$KEYCHAIN_NAME" # 6 小时不锁
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
||||
|
||||
# 3. 导入 .p12(允许 codesign 和 productsign 不弹密码框)
|
||||
security import "$CERT_PATH" \
|
||||
-P "$CSC_KEY_PASSWORD" \
|
||||
-A -t cert -f pkcs12 \
|
||||
-k "$KEYCHAIN_NAME"
|
||||
|
||||
# 4. 关键:允许 codesign 不交互访问私钥
|
||||
security set-key-partition-list \
|
||||
-S apple-tool:,apple:,codesign: \
|
||||
-s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME" >/dev/null
|
||||
|
||||
# 5. 把临时钥匙串加进搜索路径(放第一位,登录钥匙串保留作 fallback)
|
||||
security list-keychain -d user -s "$KEYCHAIN_NAME" $(security list-keychain -d user | tr -d '"')
|
||||
|
||||
# 6. 立即清理证书文件
|
||||
rm -f "$CERT_PATH"
|
||||
|
||||
# 7. 输出实际可用的 identity 名(让 sign-mac.sh 拿到精确的 CSC_NAME)
|
||||
IDENTITY=$(security find-identity -v -p codesigning "$KEYCHAIN_NAME" \
|
||||
| grep "Developer ID Application" | head -n1 \
|
||||
| sed -E 's/.*"(Developer ID Application: [^"]+)".*/\1/')
|
||||
|
||||
if [ -z "$IDENTITY" ]; then
|
||||
echo "✗ 导入后未在临时钥匙串里找到 Developer ID Application" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ 已导入证书到临时钥匙串: $KEYCHAIN_NAME"
|
||||
echo "✓ Identity: $IDENTITY"
|
||||
|
||||
# 在 GitHub Actions 里把 CSC_NAME 写入后续步骤的 env
|
||||
if [ -n "${GITHUB_ENV:-}" ]; then
|
||||
echo "CSC_NAME=$IDENTITY" >> "$GITHUB_ENV"
|
||||
echo "✓ 已写入 \$GITHUB_ENV:CSC_NAME"
|
||||
fi
|
||||
Executable
+110
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
# 一键:构建 → 签名 → 公证 → 装订 → 验证
|
||||
# 用法: pnpm sign:mac
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
R=$'\033[31m'; G=$'\033[32m'; Y=$'\033[33m'; B=$'\033[1m'; N=$'\033[0m'
|
||||
ok() { echo " ${G}✓${N} $1"; }
|
||||
fail() { echo " ${R}✗${N} $1"; }
|
||||
step() { echo ""; echo "${B}── $1 ──${N}"; }
|
||||
|
||||
# ── 前置检查(本地/CI 双模式)─────────────────────────────────────
|
||||
# 本地模式:读 .env.signing,公证走钥匙串 profile
|
||||
# CI 模式:靠环境变量 CSC_NAME / APPLE_ID / APPLE_APP_SPECIFIC_PASSWORD / APPLE_TEAM_ID
|
||||
NOTARY_ARGS=()
|
||||
if [ -f .env.signing ] && [ -z "${CI:-}" ]; then
|
||||
# shellcheck source=/dev/null
|
||||
set -a; source .env.signing; set +a
|
||||
: "${CSC_NAME:?.env.signing 缺少 CSC_NAME}"
|
||||
: "${APPLE_TEAM_ID:?.env.signing 缺少 APPLE_TEAM_ID}"
|
||||
: "${NOTARY_KEYCHAIN_PROFILE:?.env.signing 缺少 NOTARY_KEYCHAIN_PROFILE}"
|
||||
NOTARY_ARGS=(--keychain-profile "$NOTARY_KEYCHAIN_PROFILE")
|
||||
ok "模式: 本地(钥匙串 profile)"
|
||||
elif [ -n "${CSC_NAME:-}" ] && [ -n "${APPLE_ID:-}" ] && [ -n "${APPLE_APP_SPECIFIC_PASSWORD:-}" ] && [ -n "${APPLE_TEAM_ID:-}" ]; then
|
||||
NOTARY_ARGS=(--apple-id "$APPLE_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --team-id "$APPLE_TEAM_ID")
|
||||
ok "模式: CI(环境变量)"
|
||||
else
|
||||
fail "未找到签名配置"
|
||||
echo " 本地: 运行 ${B}pnpm sign:setup${N}"
|
||||
echo " CI: 设置 ${B}CSC_NAME / APPLE_ID / APPLE_APP_SPECIFIC_PASSWORD / APPLE_TEAM_ID${N} 环境变量"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ok "签名身份: $CSC_NAME"
|
||||
ok "Team ID: $APPLE_TEAM_ID"
|
||||
|
||||
# ── 1. 构建 ──────────────────────────────────────────────────────
|
||||
step "[1/5] 构建 Electron 产物"
|
||||
pnpm build
|
||||
ok "构建完成"
|
||||
|
||||
# ── 2. 打包 + 签名 ───────────────────────────────────────────────
|
||||
step "[2/5] 打包 + 签名 (electron-builder)"
|
||||
# 清理旧产物,避免 spctl 把上次未公证的 dmg 认成本次产物
|
||||
rm -f release/*.dmg release/*-mac.zip release/*.blockmap 2>/dev/null || true
|
||||
pnpm exec electron-builder --mac --arm64
|
||||
ok "打包完成"
|
||||
|
||||
DMG=$(ls -t release/*.dmg 2>/dev/null | head -n1 || true)
|
||||
ZIP=$(ls -t release/*-mac.zip 2>/dev/null | head -n1 || true)
|
||||
APP=$(ls -td release/mac*/*.app 2>/dev/null | head -n1 || true)
|
||||
|
||||
[ -z "$DMG" ] && { fail "未找到 .dmg 产物"; exit 1; }
|
||||
ok "DMG: $DMG"
|
||||
[ -n "$ZIP" ] && ok "ZIP: $ZIP"
|
||||
[ -n "$APP" ] && ok "APP: $APP"
|
||||
|
||||
# 验证签名是否真的盖上了
|
||||
if [ -n "$APP" ]; then
|
||||
if ! codesign --verify --deep --strict --verbose=2 "$APP" >/dev/null 2>&1; then
|
||||
fail ".app 签名验证失败,停止公证"
|
||||
codesign --verify --deep --strict --verbose=2 "$APP" || true
|
||||
exit 1
|
||||
fi
|
||||
ok ".app 签名校验通过"
|
||||
fi
|
||||
|
||||
# ── 3. 公证 ──────────────────────────────────────────────────────
|
||||
step "[3/5] 提交公证 (notarytool, 通常 1-5 分钟)"
|
||||
echo " 正在提交 DMG..."
|
||||
xcrun notarytool submit "$DMG" \
|
||||
"${NOTARY_ARGS[@]}" \
|
||||
--wait
|
||||
ok "DMG 公证通过"
|
||||
|
||||
if [ -n "$ZIP" ]; then
|
||||
echo " 正在提交 ZIP(用于自动更新)..."
|
||||
xcrun notarytool submit "$ZIP" \
|
||||
--keychain-profile "$NOTARY_KEYCHAIN_PROFILE" \
|
||||
--wait
|
||||
ok "ZIP 公证通过"
|
||||
fi
|
||||
|
||||
# ── 4. 装订 ticket ───────────────────────────────────────────────
|
||||
step "[4/5] 装订 ticket (stapler)"
|
||||
xcrun stapler staple "$DMG"
|
||||
ok "DMG ticket 已装订(断网也能验证)"
|
||||
# zip 不能 staple,但用户解压后里面的 .app 已经被 staple 过
|
||||
|
||||
# ── 5. 验证 ──────────────────────────────────────────────────────
|
||||
step "[5/5] 最终验证 (Gatekeeper)"
|
||||
if spctl -a -vv -t install "$DMG" 2>&1 | tee /tmp/spctl.log | grep -q "accepted"; then
|
||||
ok "Gatekeeper 验证通过"
|
||||
grep -E "source=|origin=" /tmp/spctl.log | sed 's/^/ /'
|
||||
else
|
||||
fail "Gatekeeper 拒绝了产物"
|
||||
cat /tmp/spctl.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── 完成 ────────────────────────────────────────────────────────
|
||||
SIZE=$(du -h "$DMG" | awk '{print $1}')
|
||||
echo ""
|
||||
echo "${G}${B}━━━━━━━━━━ 全部完成 ━━━━━━━━━━${N}"
|
||||
echo " 📦 $DMG (${SIZE})"
|
||||
[ -n "$ZIP" ] && echo " 📦 $ZIP"
|
||||
echo ""
|
||||
echo " 这份 DMG 可以直接发给任何 macOS 用户,"
|
||||
echo " 双击安装,首次打开${B}不会${N}弹\"无法验证开发者\"或被 Gatekeeper 拦截。"
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# 一次性签名环境配置:检查证书 + 录入 Apple ID 凭证 + 写入 .env.signing
|
||||
# 用法: pnpm sign:setup
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
KEYCHAIN_PROFILE="GeoRanklySign"
|
||||
ENV_FILE=".env.signing"
|
||||
|
||||
R=$'\033[31m'; G=$'\033[32m'; Y=$'\033[33m'; B=$'\033[1m'; N=$'\033[0m'
|
||||
ok() { echo " ${G}✓${N} $1"; }
|
||||
fail() { echo " ${R}✗${N} $1"; }
|
||||
warn() { echo " ${Y}!${N} $1"; }
|
||||
hr() { echo ""; echo "${B}── $1 ──${N}"; }
|
||||
|
||||
# ── 1. Xcode CLT ────────────────────────────────────────────────
|
||||
hr "1/3 检查 Xcode Command Line Tools"
|
||||
if xcode-select -p >/dev/null 2>&1; then
|
||||
ok "已安装:$(xcode-select -p)"
|
||||
else
|
||||
fail "未安装"
|
||||
echo " 请在终端运行 ${B}xcode-select --install${N},弹窗装好后再来"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── 2. Developer ID 证书 ────────────────────────────────────────
|
||||
hr "2/3 检查 Developer ID Application 证书"
|
||||
IDENTITIES=$(security find-identity -v -p codesigning | grep "Developer ID Application" || true)
|
||||
|
||||
if [ -z "$IDENTITIES" ]; then
|
||||
fail "钥匙串里没找到 Developer ID Application 证书"
|
||||
cat <<EOF
|
||||
|
||||
${B}申请步骤(一次性,约 10 分钟):${N}
|
||||
${Y}1.${N} 浏览器登录 https://developer.apple.com/account/resources/certificates
|
||||
${Y}2.${N} 点 ${B}+${N} 新建证书 → 选 ${B}Developer ID Application${N} → Continue
|
||||
${Y}3.${N} 它要你上传 CSR,按下面方法生成:
|
||||
打开 macOS ${B}钥匙串访问.app${N}
|
||||
菜单栏 → 钥匙串访问 → 证书助理 → ${B}从证书颁发机构请求证书…${N}
|
||||
邮箱填你的 Apple ID,常用名随意,选 ${B}存储到磁盘${N},保存为 .certSigningRequest
|
||||
${Y}4.${N} 把这个 .certSigningRequest 上传,下载得到 ${B}developerID_application.cer${N}
|
||||
${Y}5.${N} 双击 .cer,导入到 ${B}登录${N} 钥匙串
|
||||
${Y}6.${N} 重新运行 ${B}pnpm sign:setup${N}
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 多张证书时取第一张
|
||||
IDENTITY_LINE=$(echo "$IDENTITIES" | head -n1)
|
||||
IDENTITY_NAME=$(echo "$IDENTITY_LINE" | sed -E 's/.*"(Developer ID Application: [^"]+)".*/\1/')
|
||||
# CSC_NAME 必须去掉 "Developer ID Application: " 前缀,否则 electron-builder 会拒绝
|
||||
CSC_NAME_VALUE=$(echo "$IDENTITY_NAME" | sed -E 's/^Developer ID Application: //')
|
||||
TEAM_ID=$(echo "$IDENTITY_NAME" | sed -E 's/.*\(([A-Z0-9]+)\).*/\1/')
|
||||
ok "找到证书:${B}$IDENTITY_NAME${N}"
|
||||
ok "Team ID:${B}$TEAM_ID${N}"
|
||||
|
||||
if [ "$(echo "$IDENTITIES" | wc -l | tr -d ' ')" -gt 1 ]; then
|
||||
warn "钥匙串里有多张 Developer ID 证书,本脚本默认用上面这一张"
|
||||
fi
|
||||
|
||||
# ── 3. Apple ID 凭证(公证用)─────────────────────────────────────
|
||||
hr "3/3 录入 Apple ID 凭证(用于公证 notarize)"
|
||||
|
||||
NEED_CREDENTIALS=1
|
||||
if security find-generic-password -s "com.apple.gke.notary.tool.saved-creds" -a "$KEYCHAIN_PROFILE" >/dev/null 2>&1; then
|
||||
ok "钥匙串里已有 profile:$KEYCHAIN_PROFILE"
|
||||
read -r -p " 是否重新录入?(y/N): " RECONFIG
|
||||
[[ "${RECONFIG:-n}" =~ ^[Yy]$ ]] || NEED_CREDENTIALS=0
|
||||
fi
|
||||
|
||||
if [ "$NEED_CREDENTIALS" = "1" ]; then
|
||||
echo ""
|
||||
echo " 需要 ${B}Apple ID 邮箱${N} + ${B}App 专用密码${N}(不是登录密码!)"
|
||||
echo " 生成 App 专用密码: https://appleid.apple.com → 登录与安全 → App 专用密码 → +"
|
||||
echo ""
|
||||
read -r -p " Apple ID 邮箱: " APPLE_ID
|
||||
read -r -s -p " App 专用密码 (xxxx-xxxx-xxxx-xxxx): " APPLE_PWD
|
||||
echo ""
|
||||
|
||||
if ! xcrun notarytool store-credentials "$KEYCHAIN_PROFILE" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--team-id "$TEAM_ID" \
|
||||
--password "$APPLE_PWD" >/dev/null; then
|
||||
fail "凭证录入失败,常见原因:密码格式错(需带短横线),或 Team ID 不匹配"
|
||||
exit 1
|
||||
fi
|
||||
ok "凭证已存入钥匙串(profile: ${KEYCHAIN_PROFILE})"
|
||||
fi
|
||||
|
||||
# ── 写入 .env.signing ───────────────────────────────────────────
|
||||
cat > "$ENV_FILE" <<EOF
|
||||
# Auto-generated by scripts/sign-setup.sh — DO NOT COMMIT
|
||||
# 让 electron-builder 自动选用钥匙串里的证书
|
||||
# 注意:CSC_NAME 不能带 "Developer ID Application:" 前缀
|
||||
CSC_NAME="$CSC_NAME_VALUE"
|
||||
APPLE_TEAM_ID="$TEAM_ID"
|
||||
NOTARY_KEYCHAIN_PROFILE="$KEYCHAIN_PROFILE"
|
||||
EOF
|
||||
ok "写入 ${B}apps/desktop-client/$ENV_FILE${N}"
|
||||
|
||||
echo ""
|
||||
echo "${G}${B}━━━ 配置完成 ━━━${N}"
|
||||
echo " 下一步:运行 ${B}pnpm sign:mac${N} 一键打包 + 签名 + 公证"
|
||||
@@ -19,5 +19,9 @@
|
||||
"electron",
|
||||
"esbuild"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@emnapi/core": "1.9.1",
|
||||
"@emnapi/runtime": "1.9.1"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+8
-4
@@ -6,7 +6,14 @@ settings:
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
.:
|
||||
devDependencies:
|
||||
'@emnapi/core':
|
||||
specifier: 1.9.1
|
||||
version: 1.9.1
|
||||
'@emnapi/runtime':
|
||||
specifier: 1.9.1
|
||||
version: 1.9.1
|
||||
|
||||
apps/admin-web:
|
||||
dependencies:
|
||||
@@ -5157,17 +5164,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.2.0
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/runtime@1.9.1':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/wasi-threads@1.2.0':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emotion/hash@0.9.2': {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user