【免费下载链接】MacTools
A free and open-source collection of native macOS menu bar tools.
MacTools 是一款免费开源的 macOS 菜单栏工具集,把系统状态、设备电量、磁盘清理、显示控制等几十个小工具收进一个原生菜单栏面板,并支持通过插件目录自行扩展。这篇文章用通俗的语言拆解它的工程全貌:插件目录是怎么组织的、Ed25519 签名如何保证插件目录不被篡改、以及 GitHub Actions 是如何把源码一路变成"签名、公证、可自动更新"的发布产物的。
源码结构总览:一个 XcodeGen 项目如何组织
MacTools 的仓库根目录用 project.yml 描述整个 Xcode 工程,通过 XcodeGen 生成.xcodeproj,核心结构如下:
| 目录 | 职责 |
|---|---|
Sources/App/ | 主应用界面:菜单栏面板、设置、命令面板 |
Sources/Core/Plugins/ | 插件宿主:加载、校验、生命周期管理 |
Sources/MacToolsPluginKit/ | 所有插件共享的运行时框架(PluginKit) |
Plugins/ | 50+ 个内置插件,各自独立成包 |
.github/workflows/ | CI 与发布流水线定义 |
scripts/plugins/ | 插件打包、目录生成与签名脚本 |
每个插件都是"自包含"的小工程,以Plugins/DockLock/为例:
Plugins/DockLock/ plugin.json # 插件清单(身份、版本、能力、权限、本地化文案) Sources/ # 实现代码 Bundle/ # 加载入口(Factory) Tests/ # 单元测试plugin.json 是唯一的元数据源,关键字段包括id、version、pluginKitVersion(ABI 版本号)、capabilities(能贡献面板行、设置表单等)和permissions(如accessibility)。宿主在加载可执行代码之前先读这份清单,校验通过才会装载插件。
插件目录是如何运作的
MacTools 的插件不是随应用发布的,而是目录驱动:应用启动后拉取一份 JSON 目录(catalog),解析出每个插件包的下载 URL、SHA-256 校验和、最低宿主版本与所需 PluginKit 版本,再逐个校验、安装。生产目录按 ABI 版本分线路存放,例如当前源码线是 docs/plugins/v7/catalog.json,旧版本的 v4、v5、v6 目录保持不可变,老应用继续读取自己的线路,互不影响。
本地开发则走 Debug 通道:运行make build-plugin会扫描Plugins/*/plugin.json,编译全部插件并生成build/LocalPlugins/catalog.dev.json,配合file://目录 URL 即可在应用"市场"中直接安装本地包,签名校验降级为可选。这就是为什么开发者改一行插件代码后,刷新市场列表就能看到新版本。
Ed25519签名:为什么插件目录也要"防伪"
代码签名(Developer ID)保护的是应用本身,而插件目录是一份随时更新的 JSON,攻击者若篡改目录就能让用户下载恶意插件包。MacTools 为此给目录加了一道独立的 Ed25519 数字签名,实现集中在 plugin-catalog-crypto.swift:
- 规范化:签名前先移除
signature字段,再用 Foundation 的sortedKeys选项生成规范化 JSON——字段顺序统一后,同样的内容永远得到同样的签名。 - 签名:用 CryptoKit 的
Curve25519.Signing.PrivateKey(即 Ed25519)对规范化字节做签名,结果以 base64 写回目录的signature.value,algorithm标记为ed25519。 - 验签:公钥被直接嵌入应用(见 Configs/Release.xcconfig 中的
PLUGIN_CATALOG_PUBLIC_KEY),应用拉取目录后用公钥验证,不匹配就拒绝整个市场。
密钥管理上有几个值得学习的细节:
- 私钥以 base64 存放在 CI secret
PLUGIN_CATALOG_PRIVATE_KEY_BASE64,通过环境变量传入,绝不出现在命令行参数或仓库里,入口脚本是 sign-plugin-catalog.sh。 - 昂贵的构建/签名开始前,verify-plugin-catalog-key-pair.sh 会先确认"这把私钥推导出的公钥 = 应用内嵌公钥",防止误换钥匙后发布的目录全量失效。
- 它和 Sparkle 自动更新用的 EdDSA 密钥对(
SPARKLE_PRIVATE_KEY)是两把独立的钥匙:前者保目录、后者保应用更新,职责分离。
GitHub Actions发布流程:从源码到"公证上架"
流水线定义在 docs/github-actions.md 中,核心工作流如下表:
| 工作流 | 触发方式 | 产物 |
|---|---|---|
| Build | PR / main 推送 | 测试、本地化校验、网站构建(只验不发) |
| Prepare Release | 手动 | 校验版本号、编译 changelog、推 tag |
| Release | v*tag | 签名公证 DMG、GitHub Release、Sparkle appcast |
| Plugin Release | plugins-*tag | 签名插件包 + 签名目录 |
| Nightly | 每日定时 | 独立的 Nightly 应用与插件目录 |
以 release.yml 为例,一次完整的应用发布是这样走的:
- 校验 tag:tag 必须与 Configs/AppVersion.xcconfig 中的
MARKETING_VERSION完全一致,防止手滑发布错版本。 - 目录预检:运行 preflight-app-plugin-catalog.swift,确认线上插件目录与本次发布兼容且已签名——目录过期或缺失时应用发布直接中止。
- 导入证书:从 secret 还原
.p12,创建临时钥匙串,找出Developer ID Application签名身份;用完即删。 - 分层签名:先签 Sparkle 框架内部的 XPC、dylib、Finder 右键扩展,最后才签主 App 并挂 entitlements,然后
codesign --verify --deep --strict全量复核。 - 公证与装订:
notarytool向 Apple 提交 DMG 并等待回执,stapler staple把票据装订进镜像,spctl本地模拟 Gatekeeper 放行。 - 生成 Sparkle appcast:用
sign_update对 DMG 做 EdDSA 签名,写入sparkle:edSignature到 docs/appcast.xml——这就是应用内"检查更新"所读的源。 - 不可变候选 + 无凭据验证 + 发布:构建产物先上传为 immutable artifact,再由一个不带任何签名凭据的独立 job 验证 CLI 产物,最后 publish job 才创建 GitHub Release 并把 appcast 提交回 main。
插件发布 plugin-release.yml 则是"增量"思路:先由 plan-plugin-release.py 对比旧目录算出哪些插件真的变了(auto/selected/all三种模式),只构建签名选中的包,再合并进本 ABI 线路的目录、用 Ed25519 签名后提交回docs/plugins/v7/catalog.json,未变化的条目继续指向旧资产。这样一次只更新几个插件,用户也只会看到真正有新版本的条目。
新手速查:读懂这套流程的 5 个要点
- 插件 = 目录条目 + 签名包:
plugin.json声明身份与能力,目录条目提供下载链接与校验和,两层校验缺一不可。 - 签名分三把钥匙:Developer ID 签应用与插件代码,Ed25519 签插件目录,Sparkle EdDSA 签应用更新,各司其职。
- CI 只验不发:build.yml 不读任何发布 secret,发布权限收敛在专门的 publish job 中,缩小凭据暴露面。
- 发布入口统一:本地用
make release(见 Makefile),云端用 prepare-release.yml 的 Prepare Release,二者共享同一套 release.py 逻辑。 - 通道隔离:Nightly 与稳定版使用独立的 Bundle ID、目录线路、钥匙串服务与 CLI 身份,互不覆盖。
想进一步深入,建议按顺序阅读:docs/plugins/local-native-plugins.md 了解插件包结构与本地开发,docs/plugins/plugin-catalog.md 了解目录格式与发布顺序,以及 docs/github-actions.md 了解全部工作流与 secret 配置清单。
【免费下载链接】MacTools
A free and open-source collection of native macOS menu bar tools.
相关推荐
DBX 插件发布全流程:源码、未签名候选包、仓库签名与商店目录的端到端指南
DBX 插件发布全流程:源码、未签名候选包、仓库签名与商店目录的端到端指南 DBX(t8y2/dbx)的插件体系把「插件源码」「未签名审核候选包」「仓库签名安装
数据库开发者工具桌面应用CLIMCP 服务AI 应用Apereo CAS 发布工程实践:从 GPG 签名到 GitHub Actions 的完整发布流程
Apereo CAS 发布工程实践:从 GPG 签名到 GitHub Actions 的完整发布流程 本文基于 Apereo CAS 官方的发布流程文档 Rel
后端认证鉴权单点登录DBX 插件发布全流程:从源码仓库、未签名候选包到商店签名发布(RELEASING 指南深度解析)
DBX 插件发布全流程:从源码仓库、未签名候选包到商店签名发布(RELEASING 指南深度解析) 导读 本篇技术指南围绕 dbx 仓库中的 plugins/R
数据库客户端数据库桌面应用CLI后端MCP 服务AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考