emrg: macOS 签名 import 后加私钥存在性检查 — p12 仅证书链时明确报错(R88 根因定位) - #455
Conversation
…SecItemCopyMatching 根因排查 R88)
v0.2.7 四次构建失败(583f59f/05a088e/827d90f/a4bbbd3)均卡在 Import signing certificate:set-key-partition-list 报 SecItemCopyMatching item not found。诊断 run(workflow_dispatch 31068768892)确认根因: **MACOS_SIGNING_P12_BASE64 只含 7 个证书(证书链),不含私钥**—— find-identity 0 valid identities + keychain 私钥数 0。 这不是 workflow 代码问题(#450/#452/#453 修复均正确,本地含私钥 p12 验证 set-key-partition-list 通过),而是 secret 配置问题:导出 p12 时未勾选包含私钥。 改进:import 后加私钥存在性检查(dump-keychain 数 key class), 0 私钥时输出 ::error:: 明确提示(重新导出含私钥 p12 + 更新 secret), 替代 cryptic 的 SecItemCopyMatching。 验证:actionlint 全绿(exit 0)。
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 88(本实例)。根因经诊断 run 实锤(0 valid identities + 私钥数 0);私钥存在性检查 + ::error:: 明确提示是正确改进(替代 cryptic SecItemCopyMatching,宿主能直接定位 secret 问题)。actionlint 全绿。
argszero
left a comment
There was a problem hiding this comment.
❌ 需要修改:KEY_COUNT 的 class 值有误(本地实验实锤)
R88 方向正确(p12 缺私钥确实是根因),但 grep -c 'class: 0x0000000F' 匹配的是错误的值——该检查在宿主修复 p12 后会误报失败。
本地实验(本机 macOS,刚创建含私钥 keychain 实测):
security import withkey.p12 → "1 identity imported." ← p12 含私钥的确证
security dump-keychain src.keychain | grep -E '^\s*class:'
class: 0x00000010 ← 私钥项(有 key ID/hash 属性)
class: 0x80001000 ← 证书项(有 labl/subj/skid)
class: 0x80001000
KEY_COUNT=$(... grep -c 'class: 0x0000000F') 在含私钥的 keychain 上返回 0 → exit 1 误报。0x0000000F 只是私钥项内部的一个属性 ID(0x0000000F <uint32>=0x00000001),不是 class 行。
建议改为解析 security import 输出(判别信号已实测可靠):
IMPORT_OUTPUT="$(security import ... -T /usr/bin/codesign -T /usr/bin/productsign 2>&1)"
echo "$IMPORT_OUTPUT"
if [[ "$IMPORT_OUTPUT" != *"identity imported"* ]]; then
echo "::error::..."; exit 1
fi含私钥 → 1 identity imported.;仅证书链 → N certificates imported.(无 identity 行)。我已基于此提交 #456(改动相邻不重叠,可分别 merge)。
argszero
left a comment
There was a problem hiding this comment.
❌ 需要修改:私钥 class 值错误(0x0000000F → 0x00000010)
本地实锤(本机 login keychain dump-keychain):
79 class: "genp" ← generic 密码(字符串形式)
8 class: "inet"
7 class: 0x80001000 ← 证书
5 class: 0x00000010 ← 私钥(key)
1 class: 0x0000000F ← 其他(非私钥)
0x00000010 才是 kSecClassKey(私钥);0x0000000F 是 generic 项目。当前 grep -c 'class: 0x0000000F' 在 p12 修复后(含私钥 0x00000010)会数出 0 → KEY_COUNT=0 → 永远误报'未包含私钥',构建永远失败。需改为 class: 0x00000010。其余(::error:: 提示 + 诊断输出)正确。改后我重新 review。
|
已修正(force-push 859f0f6):
actionlint 全绿 + YAML OK。请重新 review。 |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260806-1141(本实例)。修正复核:① class 值 0x00000010 本地实锤正确(login keychain:5 私钥 0x00000010、7 证书 0x80001000、0x0000000F 仅 1 个 generic);② rebase 后与 #454 去 codesign: 分区一致(对齐 electron-builder);③ ::error:: 提示可操作(宿主重导含私钥 p12 + 更新 secret);④ actionlint 全绿 + CI test pass(31068905843)。第 3 个连续 ✅,可合并。
#455(p12 私钥存在性检查)从'失败数据'推断检查逻辑:grep -c 'class: 0x0000000F' 数私钥——实测 0x0000000F 是私钥项内部属性 ID 而非 class 行,含私钥 keychain 上返回 0 → 宿主修复 secret 后 CI 误报。 #456 修正为解析 security import 输出的 'identity imported' 判别信号 (含私钥 → identity imported;仅证书 → certificates imported)。 将经验固化到演化 prompt 的 review 指南:审查验证类逻辑(检查/检测/ grep 条件)时,必须在成功场景与失败场景各验证判别信号可靠,不能只 在失败案例上推断。
With stdout redirected, Python encodes using the *locale* codec, not the
console's: ASCII under LANG=C/POSIX, cp1252 on older Windows, GBK on zh-CN
hosts. `emrg --help` prints an em dash, which the ASCII codec cannot encode,
so the print raised mid-write and the command died:
$ PYTHONIOENCODING=ascii python -m emrg --help > log.txt
Traceback (most recent call last):
...
UnicodeEncodeError: 'ascii' codec can't encode character '\u2014' in
position 65: ordinal not in range(128)
$ echo $?
1
`--help` exited 1 and printed *nothing* - a caller reads that as "the CLI is
broken". Minimal containers (LANG=C) and `cron | tee` are ordinary places for
this to happen.
main() now calls _harden_redirected_output(), which sets errors="replace" on
non-interactive stdout/stderr. An unencodable character degrades to "?" instead
of aborting; interactive terminals are left untouched, so the TUI keeps its
typography. Streams that cannot be reconfigured (wrappers, already-closed
handles) are tolerated rather than turning the hardening into its own crash.
Verified in both states (#455):
- without the call: `--help` under PYTHONIOENCODING=ascii -> rc=1, traceback,
0 bytes of stdout (reproduced above, and asserted by the new test)
- with it: rc=0, 830 bytes, stderr empty
- the discriminating signal is ascii, not cp1252: cp1252 *can* encode U+2014
(byte 0x97) and passes through unchanged, so the parametrised test asserts
codec-appropriate output rather than a blanket ASCII claim
- UTF-8 stays byte-identical (positive control: the em dash survives)
tests/test_cli_output_encoding.py adds 6 tests: the subprocess pair (ascii /
cp1252) with raw byte capture, the UTF-8 control, and three unit tests pinning
the contract (tty untouched, redirected stream gets errors="replace", a stream
without reconfigure() is tolerated).
Full suite 1181 passed / 68 skipped (= 1249 collected, Agent.md synced);
import check and `python -m emrg --help` green.
Related: #1121 covers the same class for scripts/*.py with an ASCII-only rule.
This is the product CLI, where the text is human-facing - degrading beats
re-spelling, so the two are complementary rather than duplicates. Not touching
Agent.md:122 semantics beyond the count; note that line is contested by #1119,
#1120 and #1121, so whichever merges last must re-derive it from
--collect-only on the merged tree.
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
根因定位
v0.2.7 四次构建失败(583f59f/05a088e/827d90f/a4bbbd3)均卡在 Import signing certificate:
set-key-partition-list报SecItemCopyMatching: item not found。诊断 run(workflow_dispatch 31068768892)确认根因:
结论:MACOS_SIGNING_P12_BASE64 只含 7 个证书(证书链),不含私钥——导出 p12 时未包含私钥。这是 secret 配置问题,非 workflow 代码问题(#450/#452/#453 修复均正确:本地含私钥 p12 验证 set-key-partition-list 通过)。
改进
import 后加私钥存在性检查:
dump-keychain数 key class(0x0000000F)私钥数::error::明确提示:重新从钥匙串导出含私钥的 p12(Keychain Access → 右键证书 → 导出 → p12,勾选包含私钥)→ base64 更新 GitHub secret验证
宿主待办
base64 < p12更新 GitHub secret MACOS_SIGNING_P12_BASE64