Issue 190 - ShaoClean/Alune GitHub Wiki
Issue #190:GPG / SSH 提交签名与验证状态
- 类型:功能
- 状态:已实现,待审阅;真实 GPG、桌面 pinentry 与跨平台原生现场待验收
- Issue:https://github.com/ShaoClean/Alune/issues/190
- PR:https://github.com/ShaoClean/Alune/pull/202
- 最近更新:2026-10-06
- 代码基线:
development/9653431709afec3dc4d8f41a07892a188f10221d - 实现提交:
6f7db3697d26b59dfa4c58e3edd6d9df9c8ea9b3;测试修正:ada5f7b
背景与目标
原提交操作依赖 Git 配置,签名失败直接暴露底层错误,历史无法显示验证状态;标签创建还强制禁用签名。本次提供仓库级签名配置、签名提交与附注标签、修改上一次提交,以及历史和详情验证结果。不生成或管理私钥,不收集口令。
方案与取舍
- 工具条「提交签名设置」读取实际生效的
commit.gpgsign、gpg.format、user.signingkey,支持 GPG / SSH。保存只写仓库配置;密钥留空会移除本地覆盖,重新继承全局配置或 Git 默认值。 - 普通提交与修改上一次提交遵循 Git 的签名配置。修改前确认要替换的 HEAD,若发生变化则拒绝操作;成功后刷新历史。已有签名随修改重新生成。
- 附注标签在提交签名或
tag.gpgsign开启时签名,轻量标签保持不签名。附注结尾补足签名分隔换行;兼容旧 Git 在 SSH 签名失败时返回 0 的行为,检查生成对象并撤销未签名标签。标签列表去除签名正文,仅展示附注。 - 签名失败保留 Git 原因,给出缺失密钥、pinentry 和 agent 的处理建议,不降级为不签名重试。
- 验证与创建均通过既有本地 / SSH transport 执行。SSH 仓库的程序、密钥路径、agent、信任文件属于远端主机,设置弹窗明确提示。
- 历史只对可见提交进行批量验证,每次最多 100 个完整哈希,验证有超时和输出限制。详情按需验证;刷新清除客户端验证结果,避免信任文件修改后仍显示旧结果。
- 使用
%G?、%GS、%GK获取状态、签名者和密钥。G 为有效,B/X/Y/R 为无效并展示具体原因,U/E 为未知密钥或信任,N 为未签名。Git 2.43 在 SSH 信任文件缺失时可能错误返回 N,额外批量检查原始提交头,避免已签名提交显示为未签名。 - 本机验证结果依赖当前信任配置,不保证与 GitHub / GitLab 的 Verified 一致。
验收标准
- 仓库内读写 GPG / SSH 格式与签名开关。
- 本地及隔离 SSH 通道的 SSH 签名提交、amend、附注标签可由 Git 验证。
- 密钥缺失、agent 不可用、模拟 pinentry 失败时给出处理建议,提交失败保留 HEAD 与暂存内容。
- 历史与详情区分有效、无效、未知密钥 / 信任、未签名;包含篡改和信任变化验证。
- 远端执行说明与 390px 设置界面检查。
- Linux / macOS CI 的本地及隔离 SSH、Windows CI 的本地真实 GPG 密钥签名、amend、标签验证。开发机缺少 GPG,其本地运行仍跳过对应两项。
- macOS / Windows / Linux 原生桌面 pinentry、agent 与各平台真实远端配置验收。
- 大规模实际仓库的签名验证延迟基准。
验证记录
2026-10-06,macOS arm64,Node 25.5.0,Git 2.43.1。SSH 测试使用本机隔离 SSH 服务,Git 与文件操作为真实执行;不是独立远程生产主机。测试密钥与仓库均为临时数据。
| 代码 / 场景 | 结果 |
|---|---|
| 当前独立分支:shared、ssh-client、server、web 构建 | 通过;前端保留既有大体积 bundle 提示 |
当前独立分支:npm test -w web |
259 项通过 |
当前独立分支:node --test packages/ssh-client/tests/signing.test.cjs packages/ssh-client/tests/tags.test.cjs apps/web/tests/signing.integration.cjs |
12 项通过,2 项真实 GPG 用例跳过;签名、标签、HTTP 在本地及隔离 SSH 验证 |
| 实现阶段:签名、标签、历史及暂存回归 | 31 项通过,2 项真实 GPG 用例跳过 |
实现阶段:npm test -w server -- --runInBand local-git.spec.ts |
19 项通过 |
| 实现阶段浏览器:历史、详情、修改提交及刷新 | 有效 / 篡改无效 / 未签名正确显示;详情显示签名者与指纹;修改后历史即时更新 |
| 实现阶段浏览器:远端设置与 390px 窄屏 | 远端依赖提示明确;弹窗字段与保存按钮可用 |
| 真正无终端的桌面 GPG pinentry 弹窗 | 未运行;受控错误程序仅验证错误建议和失败保留,不能替代真实交互验收 |
| Windows / Linux 原生现场 | 未运行;CI 已接入签名用例,Windows 跳过 Unix SSH fixture |
界面对照
改动前
复用 Issue #186 已发布的历史截图,展示无签名状态的历史界面;不是同一临时仓库的逐像素对照。

改动后
2026-10-06 实现阶段采集。历史中有效、篡改无效与未签名分别显示;详情截图捕获于异步验证中,随后交互核对结果为有效并显示身份与指纹。

远端仓库,390px:

风险与回退
- 无数据库迁移;保存会修改仓库 Git 配置,回退代码不会自动还原这些配置,也不会删除已经创建的签名提交或标签。
- 验证依赖执行主机的工具、密钥和信任文件。跨平台桌面交互、真实远程主机的 agent 可用性仍需现场验收。
- 关闭提交签名不会覆盖已有
tag.gpgsign,界面会说明此差异。 - Wiki 具有独立历史,回退代码时需另行核对该页面。
关联资料
2026-10-06 PR CI 补充
首轮 CI 暴露两项测试问题:控制器 mock 未包含新增 amend 参数,真实 GPG 用例的 CLI 复核沿用了创建 fixture 时捕获的环境,没有读取临时 GNUPGHOME。生产签名与签名状态断言在 GPG 用例中已通过,失败发生于随后独立 CLI 复核。ada5f7b 修正断言与复核环境;本地控制器 10 项、签名 5 项通过,2 项 GPG 仍因本机缺少 GPG 跳过。已触发 CI 重验,结果未确认前不记为通过。
Windows CI 修复
78c8e3d 将签名配置读取改为等待所有并发 Git 进程结束后再抛出错误,避免无效布尔值触发提前返回、Windows 清理仓库时仍被其他 Git 进程占用;新增受控并发回归用例。该轮 CI 的目录清理用例通过,另外 10 个测试任务通过。GPG 的 C:/... 路径仍被 MSYS 当作相对路径,因此 09e578d 进一步根据 GPG 的目录格式识别 MSYS,并用 cygpath 转为其绝对路径,保留原生 Windows GPG 的盘符路径。Windows 真实 GPG 用例继续运行,没有通过跳过测试规避失败。
本地签名回归 6 项通过、2 项 GPG 因未安装而跳过;其他本地 Git 回归 41 项通过。最终提交 09e578d3fa7bf4b5c2556936373465028a623aaa 的 PR 单元测试 CI 全部 11 个任务通过。Windows 本地 Git 共 39 项通过、0 失败、10 项既有平台跳过;真实 GPG 签名、amend、标签、无效配置清理及新增并发回归均实际执行通过。Linux / macOS 的本地和隔离 SSH 签名与 HTTP 回归也通过。原生桌面 pinentry 和独立远程主机验收仍未完成。