# 2026-10-06 Mac 远程开发/打包机落地教程（SSH 版）

> 配套正本：策略 `2026-10-01-苹果生态上架策略.md`、账号申请 `2026-10-04-Apple开发者账号申请教程.md`、开发与发布 `2026-10-04-macOS版开发与发布规划.md`
> **本文覆盖 10-04 规划里的「CI 出包」执行形态**（原因见 §一），Apple 账号路线、签名公证要求、验收口径都不变。
> 分工（10-06 与 boss 对齐）：**你在 Mac 上只做一次 §二 的三个动作（约 3 分钟），之后 mac 出包、签名、公证、上 CDN 全由我从这台 Windows 远程执行，你零参与，密钥不出 Mac。**

## 〇 · 连接信息（正本）

Qoder 侧不显示 SSH 连接，一切以这张表为准；`~/.ssh/config` 里 2026-10-06 已登记别名。

| 项 | 值 |
|---|---|
| 别名 | `mac` / `mac-build` |
| 地址 | `192.168.31.10:22`（局域网 DHCP，非固定 IP；换了只改 config 里 `HostName` 一行）**2026-10-16 boss 换 IP：192.168.31.220 → 192.168.31.10，旧值作废** |
| 账号 / home | `linxianqing` / `/Users/linxianqing` |
| 主机名 / 系统 | `linxianqingdeMacBook-Pro-2.local`，macOS 27.0.1 / arm64 |
| 私钥 | Windows `~/.ssh/mac_build_ed25519`（公钥在 Mac `~/.ssh/authorized_keys`，`IdentitiesOnly yes` 不会拿别的钥匙去试） |
| 免密验证 | `ssh mac 'hostname'` 回 `linxianqingdeMacBook-Pro-2.local` —— 10-06 19:35 实跑通 |
| Mac 工作区 | 源码 `~/work/easy_image`，出包驱动 `~/mac_ei_build.sh`，日志 `~/ei-macbuild.log` |
| Apple 四件套 | `~/Desktop/apple开发/`，就地读取，**不出这台机** |
| 回滚 | 删 Mac `authorized_keys` 那 1 行公钥 + 删 `~/.zprofile` 里 `# pinnadash build env` 那两行 + 删 `~/work`、`~/mac_*.sh`、`~/*.log` = 回到接手前 |

我用的命令面就三条（Windows Git Bash）：

```bash
ssh mac '命令'                      # 远程执行
scp 本地文件 mac:~/                 # 推代码/脚本
scp mac:apps/desktop/src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg ./   # 拉产物
```

---

## 一 · 形态为什么这样定

三条事实决定的，不是偏好：

1. **mac 的包只能在 macOS 上打**。`codesign`/`notarytool` 是 Apple 工具链，Tauri 也无法从 Windows 交叉产出 `.app`/`.dmg`。Windows 这台继续打 Windows 的 `.exe`，一条命令不变。
2. **四件套现在都在你的 Mac 上**（`.p12`/`.p8` 是在 Mac 钥匙串里生成的，`.p12` 里的私钥只跟那把钥匙串同源）。把它们搬到 Windows = 多一份私钥副本 + 一次跨机传密，安全性下降、收益为零。
3. **CI 那条路有前置缺口**：10-04 规划走 GitHub Actions mac runner，而五仓目前只有 NAS 仓、没有 GitHub remote（这条缺口当时就记在规划里）。为了出第一个 mac 包去补 GitHub 授权，是把顺序倒过来了。

所以：**你的 Mac = 远程 mac 打包/开发机，我用 SSH 进去干活**。CI 不废弃，回归条件见 §六。

| 环节 | 机器 | 谁执行 |
|---|---|---|
| 写码 / 自测 / Windows exe | 这台 Windows（192.168.31.44） | 我，全自动 |
| mac dmg + 签名 + 公证 | 你的 Mac，**我 SSH 进去跑** | 我，全自动 |
| mac 包上 R2 + 更新 feed + 官网点亮 | 这台 Windows `release.mjs` | 我，全自动 |
| 装上双击看一眼 | 你的 Mac | 你（验收，想验的话） |

---

## 二 · 你要做的（一次，3 分钟）——**10-06 19:00 已全部完成**

1. **开远程登录**：Mac 系统设置 → 通用 → 共享 → **远程登录** 打开。✅ 已开，历史 IP `192.168.31.220:22` 实测握手 `SSH-2.0-OpenSSH_10.3`（**2026-10-16 起该机 IP 变为 `192.168.31.10`**，见本文顶部「地址」行）
2. **把它的局域网 IP 告诉我**。✅ 原 `192.168.31.220`（2026-10-16 变更后为 `192.168.31.10`），登录账号实测为 `linxianqing`（不是 `lin`），home=`/Users/linxianqing`，macOS 27.0.1 / arm64
3. **认一把钥匙**。✅ 走 A：Windows 侧用一次性密码认证把公钥 `~/.ssh/mac_build_ed25519.pub` 写进 Mac 的 `~/.ssh/authorized_keys`（1 行，权限 600），此后 Windows→Mac 全部免密；Mac 上原有的 `id_rsa`/`id_ed25519`/`config` 一个没碰

这三步之外不需要你做别的。我不会往 Mac 上装来路不明的东西，也不会改它的系统设置（远程登录那一处除外，是你自己开的）。
装的东西全在 `~` 下、不碰系统、不要 sudo：`~/.local/node`（node v20.19.0）、corepack 的 pnpm 9.15.4、`~/.cargo`+`~/.rustup`（rust 1.99.0，两个 mac target 齐）；
PATH 追加写进 `~/.zprofile` 一处，开头有 `# pinnadash build env` 标记，删那两行即回滚。

---

## 三 · 之后我的动作链（全远程，自动）——**10-06 晚实跑回填**

1. Mac 装开发环境 ✅（Xcode CLT 你机器上本来就有 `/Library/Developer/CommandLineTools`，**没装 Homebrew**——直接装 node/rust 更短，少一个后台常驻）
2. ~~从 NAS clone~~ → **改走 git bundle**：NAS 的 git 匿名读是 401，为出第一个包去挂 deploy key/只读 token 属倒序；本机 `git bundle`（1.2MB）scp 过去 `clone` 到 `~/work/easy_image`，HEAD 与 Windows 侧同为 `4693a09`、工作树干净。等 mac 线成为日常，再按原计划补 NAS 只读凭据。
   **npm 私有源要凭据**：`@pinnadash/*` 包在 NAS 上（匿名 401），已把 Windows `~/.npmrc` 里那一行 NAS token 原样搬到 Mac 的 `~/.npmrc`（只搬这一行，别的 token 不带）。⚠ 收口项：换一把**只读** deploy token，别长期用全权 token 的第二副本。
3. 跑 `build-mac.sh --universal` ✅ 入口用仓内正本，远程侧只加一层驱动脚本 `~/mac_ei_build.sh`（注入签名/公证环境变量 + 注入 `.p12` 签名凭据 + 调 `build-mac.sh`），不重拼构建链
4. 签名公证 ⚠ **19:35 A/B 取证推翻原判断**：`security unlock-keychain` + `set-key-partition-list` 这两行**不足以让纯 SSH 会话签通 login 钥匙串**——实测仍 `errSecInternalComponent`，产物停在 `Signature=adhoc / TeamIdentifier=not set`，`spctl` 直接 reject。同一身份两种载体对照：login 钥匙串 = FAIL；用 `证书.p12` 现场导入建的专用构建钥匙串 = `Authority: Developer ID Application: Xianqing Lin (395L3P686G)`、`TeamIdentifier=395L3P686G` 签通。定型：**走 Tauri 自带的 `.p12` 注入**（`APPLE_CERTIFICATE` = base64(p12) + `APPLE_CERTIFICATE_PASSWORD`），它内部起临时钥匙串、用完即弃——不碰你机器上的默认钥匙串，也不再需要登录密码。公证走 Apple ID + App 专用密码（`.p8` API key 留着备用）。
   四件套实测位置（就地不动、不外传）：`~/Desktop/apple开发/` = `证书.p12` + `p12-password.txt` + `AuthKey_5WJ8AZ23UT.p8` + `appstoreconnect.txt`(Issuer ID + 秘钥 id 5WJ8AZ23UT) + `apple-id.txt` + `app专用密码.txt`；值一律在 Mac 上就地读，不进日志、不进 git。
5. ~~产物走 SMB 推回 Windows~~ → **改为 Windows 从 Mac 拉**（Mac 的 sshd 已在，Windows 侧没有 sshd，反向推要多开一个服务）：`scp` 拉 dmg/`.app.tar.gz`。
6. 后续波次 EF → ED → ES → EC → Linux 照 10-04 规划推
7. **2026-10-16 系统管家（system_butler）接入 mac**：第 5 个走这条链的产品，配方原样复用（target 隔离 `windows`/`winreg` 依赖、
   `tauri.macos.conf.json` 覆盖、仓内 `build-mac.sh`、`tools/mac_sync_src.sh` 同步）。新增两件可复用物：
   ① 真机冒烟闸门 `tools/mac_app_smoke.sh`（进程活 / 日志落盘 / **前端真渲染** 三判据——第三项靠「日志里出现只可能由前端 JS 调后端产生的行」，
   专堵「进程活着但界面是白板」骗过验收）；② 平台能力表 + 命令面机检（mac 可见页面只能调 mac 命令表里的命令，
   Windows 专属调用必须挂在 `platform.has(block)` 门后）。P0 纵切判据与实测结果见
   `system_butler/docs/plans/2026-10-16-Mac版落地方案.md`。

### 首跑踩到的两个真问题（都已修，见下）

- **`tauri.macos.conf.json` 里的 `"//"` 注释键把 mac 构建直接闸死**：Tauri 按 schema 严格校验平台覆盖文件，未知属性报 `Additional properties are not allowed ('//' was unexpected)`；而这份文件**只在 macOS 构建时才参与合并**，所以 Windows 五仓一路绿灯、mac 真机首跑才红。修法：注释移出 JSON，口径写进 `easy_image/docs/MACOS-BUILD.md`。**其余四仓同文件同病，波次推进时一起改**。
- **国内网络两条下载慢链路**：`static.rust-lang.org` 直连实测 69KB/s（rustup 卡 20 分钟不动），改 `rsproxy.cn` 镜像后 850KB/s；crates.io 同理走 `sparse+https://rsproxy.cn/index/`（注意索引路径是 `/index/`，写成 `/crates.io-index/` 会 404 且报 `no matching package named serde`）。配置落 Mac 的 `~/.cargo/config.toml` 与 `~/.zprofile`。

---

## 四 · 验收口径（不变）

EI mac 版结票四项：**能装上 / 能启动 / 点登录能跳浏览器并回跳应用（`imagefactory://` 深链）/ 检查更新能弹窗**。
签名+公证做完后，第一项不用右键强开，Gatekeeper 直接过；只做本机验收时未签名包也能装（右键 → 打开）。

---

## 五 · 已知的坑（都核过原文/脚本）

- **App Store Connect API Key 角色必须 `App Manager` 及以上**，选 Developer 公证直接 403（10-06 核 Apple 文档）
- **`.p8` 只有下载那一刻能拿到**，丢了只能删掉重建一把 Key
- **`.p12` 别用文本编辑器打开另存**，二进制会损坏
- **走 updater 的 `.app.tar.gz` 必须签名+公证**，没签名装不上（10-04 规划 §2.4）
- **Apple 过审/续费邮件有钓鱼仿冒**：只看 `developer.apple.com/account` 页面状态，邮件里的链接不点
- **Mac 上同样吃端口注册制**：dev server 必须显式端口 + `strictPort`，别拿 5173 当默认（`ops/ports.json` 正本）
- **SSH 会话无 GUI：login 钥匙串即使手动解锁 + 放行分区列表也签不动**（`errSecInternalComponent`，10-06 实测）；签名一律走 `.p12` 注入，别再去解 login 钥匙串（§三 第 4 条）

---

## 六 · CI 什么时候再回来

现在这台 Mac 承担 mac 构建，代价是**发版那一刻 Mac 得开着、在线**。以下任一成立就回 10-04 的 CI 路线（那时再把四件套交回一次，配进 GitHub Secrets）：

- mac 包月构建需求 > 10 次（GitHub 私有仓 macOS runner 按 10x 计费，免费额度≈200 分钟/月，一次 15-25 分钟）
- 要 mac 发版也进无人值守链路（公司=常驻自动机器，不依赖某台机器开着）
- 产品仓接上 GitHub remote 且 Actions 权限就位

在此之前，Mac 打包机的路径更短、零额度成本、且私钥不出机。
