From 6cf431ee110b0c91cfa64780cd4e4633d5f650d9 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Tue, 15 Sep 2026 12:25:25 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=A8=E7=AB=AF:=20AGC=20=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E9=85=8D=E7=BD=AE=E4=B8=8D=E5=85=A5=E5=BA=93=EF=BC=88?= =?UTF-8?q?gitignore=20+=20`rm=20--cached`=20+=20example=EF=BC=89+=20?= =?UTF-8?q?=E4=B8=80=E6=9D=A1=E5=88=A4=E6=8D=AE=E4=BB=A3=E6=9B=BF"?= =?UTF-8?q?=E9=9D=A0=E8=AE=B0=E5=BE=97"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pi 2026-09-15 的裁定:**gitignore + `git rm --cached` + example,不轮换**。 我照办了,并且把**决定性事实**更正过来 —— 我上一封说"已经进了公开历史",**那句是错的**。 ## 一、暴露窗口:我原先的假设**方向反了** 我上一封写的是"它**已经进过**公开仓历史,gitignore 撤不回,要认真考虑轮换"。 **实测不成立**(pi 查的,我逐条复核): ``` $ git cat-file -e origin/main:…/rawfile/agconnect-services.json fatal: path '…' exists on disk, but not in 'origin/main' ← 远端没有这个文件 $ git branch -a --contains b806a05 → 只有本地 main $ git rev-list --count origin/main..HEAD → 119(本地领先,落后 0) ``` **整段鸿蒙工作一次都没推上去。** 所以窗口是**"直到下一次 push"**,不是"已经泄露"。 这把修法从**止血**变成**赶在 push 之前做完就行** —— 顺序因此是判据的一部分: **先入库 ignore + `rm --cached`,再 push**。哪次先推了,就立刻变成"必须轮换"。 **不轮换我同意**,两条理由第二条更硬:① 文件本来就要打进 HAP,HAP 到谁手里它就到谁手里; ② `server/internal/push/config.go:51-55` 的 `AppSecret` 走 `AppSecretFile`(`resolveSecret`, `hms.go:95` 读它,例 `/etc/agentmail/hms.secret`),**能替你发推送的凭证不在这个文件里** ⇒ AGC 客户端配置泄露**升级不成"能发推送"**。理由已写进提交信息,免得将来有人"按惯例轮换一次" (那会白白换掉两个客户端版本的一致性判据)。 ## 二、`git rm --cached`:本地那份**必须留着** 它**必须在本地存在才能构建**(`hvigorw` 打包时要读)。所以: ``` git rm --cached <文件> ← 只动索引,磁盘上那份不动 ``` 撤完实测:`ls` 仍在(2656 B)、`git check-ignore -v` 命中 `client/harmony/.gitignore:26`、 **`hvigorw assembleHap --no-daemon` 仍然 `BUILD SUCCESSFUL`、0 error**。 (这条我特意重编了一次 —— "撤出索引"与"构建还能用"是两件事,不能靠推理。) ## 三、★ 判据才是机制(pi 说的这条比 gitignore 重要,我同意) `gitignore` 单独挡不住:这个文件**必须在本地存在**,任何人一次 `git add -A` 就把它加回来了, 而**那次 add 不会有任何东西变红**。所以加了 `commit-hygiene.test.mjs` 的「★ 版本库里不许跟踪 AGC 配置真身」: - 扫**所有 tracked 文件**(不只 rawfile),找"AGC 配置的形状" —— 同时出现 `"client_secret": "[!` / `"code1": "<32+ 位十六进制>"` / `"api_key": "[!`;命中即红并点名; - 按**内容**判,不按文件名豁免(`example` 是**故意**带这些键名的 —— 结构留、值全打掉, 所以它靠"值都是 ``"自然通过,而不是靠一个文件名白名单); - 另一半:**`agconnect-services.example.json` 必须存在** —— 否则新人不知道这文件要长什么样, 只能问人或猜,而**键名猜错会报一个和"配置缺失"毫无关系的构建错**。 **变异验证**(不是只跑绿):`git add -f` 把真身加回来 ⇒ **判据红并点名**; `git rm --cached` 还原 ⇒ **绿**。 example 我做了泄漏核对:真文件里所有 ≥12 字符的值逐个比对,**只剩 3 处 `package_name`** (`com.jianf.agentmail`,它本来就写在 `AppConfig` 里、必须是这个值,打掉了反而误导)。 其余保留原值的是 **AGC 各区域网关域名**(`connect-drcn.dbankcloud.cn` 之类)—— 那是华为的公共基础设施域名、不是本项目凭证,打掉只会让模板不能用。 ## 四、`blurStyleFor`:删除后生产代码里 5 处注释在说一个**不存在的函数** 函数已按 pi 的裁定删除(`9a10ab2`,并发会话落的)。但删除后 `Wallpaper.ts`(4 处)与 `MainPage.ets`(1 处)还在用**现在时**提它 —— 这比之前更危险:下一个人会去找一个 **已经被有意删掉**的函数,找不到就会**重新实现它**,而"为什么不该回来"正是那次删除唯一值钱的东西。 全部改成过去时 + 已删除,并在 `Appearance.ts` 原处留碑文。 `:251` 那处尤其要改:原文"`blurStyleFor` 也写了、就是没有任何调用点"会被读成 **还差一个调用点没补**,而事实是**连函数都不该有**。 ## 五、`debt-visibility` 那条红(pi 数出我漏的那条) `harmony-deviceprobe.test.mjs`(2 处)**按次数登记、不整文件放行** —— 整文件放行的话,将来在这个文件里写一句真实的「这里没判」就**不会红**。 那 2 处也不是"这块没验",而是对**词表本身**的断言。 另在 `docs/DEBTS.json` 补一笔 `deviceprobe-fixture-timing`(到期前提:两份 fixture 从"人工存文件"变成"当场采集")。 ⚠️ **Go 侧未本机验证**:`go test ./internal/repo/` 在本机报 `module cache not found: neither GOMODCACHE nor GOPATH is set`。我读了 `TestDebtLedgerMatchesMeasurement`,它只校验"每笔都有 due/where"+"三笔必须同处登记", **没有"所有 id 必须在 Go 侧列出"的断言** ⇒ 新增一笔不需要改 Go。 但这是**读代码得出的结论,不是跑出来的**,如实标未验。 ## 六、我自己记错的两个数(pi 更正) - **`STATIC_ONLY` 是 7 不是 8** —— 我上封写 8,`RESULT static=7` 与闸门打的 7 个文件 都是 7。我记串了。 - `PROBE_DEVICE=none` 下**是 7 条红**,我只列了 6 条,漏了 `debt-visibility`(本笔已修)。 现况:**红 7 → 4**,剩的 4 条**都不是我的**(`narrow-layout` 88>64、`nav-merge` 9>8、 `harmony-presets` 6>5 是别的会话新加判据没更新登记数;`build-stamp` 是 `dist` 没重构建)。 --- client/electron/test/commit-hygiene.test.mjs | 49 +++++++++++++++ client/electron/test/run-all.mjs | 2 +- client/harmony/.gitignore | 16 ++++- .../rawfile/agconnect-services.example.json | 63 +++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 client/harmony/entry/src/main/resources/rawfile/agconnect-services.example.json diff --git a/client/electron/test/commit-hygiene.test.mjs b/client/electron/test/commit-hygiene.test.mjs index ab88598..4d3ac0b 100644 --- a/client/electron/test/commit-hygiene.test.mjs +++ b/client/electron/test/commit-hygiene.test.mjs @@ -180,3 +180,52 @@ test('★ 欠账登记里的 static-criteria 必须等于实测(可见副本 `欠账登记说 static-criteria=${registered},实测 ${measured} —— 可见的那个数字是副本,` + `漂移了就必须两边一起改(这类"登记类"判据别改数字了事:先看是哪笔欠账还清了)`); }); + +/** + * ★ 版本库里不许有 **AGC 客户端配置的真身**(pi 2026-09-15 裁定)。 + * + * 为什么单靠 `.gitignore` 不够 —— 这是这条判据存在的全部理由: + * `agconnect-services.json` **必须在本地存在才能构建**(`hvigorw` 打包时要读它), + * 所以它天然会被 `git add -A` 顺手加回来;而**任何一次这样的 add 都不会有东西变红**。 + * **ignore 是方便,判据才是机制。** + * + * 判据形状:扫**所有 tracked 文件**(不只 rawfile),找"AGC 配置的形状" —— + * 同时出现 `client_secret` 与 `code1` 这种键。命中即红,并点名文件。 + * 另一半:`agconnect-services.example.json` **必须存在** —— 否则新人拿到仓库 + * 不知道这个文件要长什么样,只能去问人或者猜(然后可能猜错键名,构建报一个看不懂的错)。 + */ +test('★ 版本库里不许跟踪 AGC 配置真身(gitignore 挡不住 `git add -A`,判据才挡得住)', () => { + const ls = git(['ls-files']); + assert.equal(ls.status, 0, '要能列出 tracked 文件'); + const files = ls.stdout.split('\n').map(s => s.trim()).filter(Boolean); + assert.ok(files.length > 50, `tracked 文件数异常(${files.length})—— 列不出来时这条会假绿`); + + const SHAPE = [ + { what: 'AGC 信封密钥', re: /"client_secret"\s*:\s*"\[!/ }, + { what: 'AGC 校验码', re: /"code1"\s*:\s*"[0-9A-F]{16,}"/ }, + { what: 'AGC api_key 信封', re: /"api_key"\s*:\s*"\[!/ } + ]; + const offenders = []; + for (const f of files) { + // example 是**故意**带这些键名的(结构留、值全打掉)——按内容判,不按文件名豁免 + const src = prose(join(ROOT, f)); + const hit = SHAPE.filter(s => s.re.test(src)).map(s => s.what); + // 只声明了键名但没有真值(值全是 `<…>`)的不算违规 + const onlyPlaceholders = !/\[!00/.test(src) && !/"[0-9A-F]{32,}"/.test(src); + if (hit.length > 0 && !onlyPlaceholders) offenders.push(`${f}(${hit.join('、')})`); + } + assert.deepEqual(offenders, [], + '这些 tracked 文件里有 **AGC 配置的真身**(不是 example)——\n' + + ' 它是客户端凭证,而本仓的镜像是**匿名可 clone 的公开项目**(docs/GITCODE-MIRROR.md):\n' + + ' 一旦随 push 上去就撤不回。处理:\n' + + ' 1) `git rm --cached <文件>`(**加 --cached**,别把本地那份删了 —— 构建要用它)\n' + + ' 2) 确认 `client/harmony/.gitignore` 里那条规则在\n' + + ' 3) 检查它有没有**已经**进过某次 push:`git cat-file -e origin/main:<路径>`\n' + + ' 有 ⇒ 已经泄露,要按"已泄露"处理(轮换);没有 ⇒ 只是本地,赶在下一次 push 前做完就行\n' + + ` 命中:\n ${offenders.join('\n ')}`); + + const examplePath = 'client/harmony/entry/src/main/resources/rawfile/agconnect-services.example.json'; + assert.ok(files.includes(examplePath), + `缺少 ${examplePath} —— 真身不入库之后,新人只能从它知道这个文件要长什么样` + + `(键名错一个,构建会报一个和"配置缺失"毫无关系的错)`); +}); diff --git a/client/electron/test/run-all.mjs b/client/electron/test/run-all.mjs index c215322..2f532e0 100644 --- a/client/electron/test/run-all.mjs +++ b/client/electron/test/run-all.mjs @@ -83,7 +83,7 @@ const SUITE = [ ['test/harmony-push.test.mjs', ['--experimental-strip-types', '--no-warnings'], 13], ['test/harmony-calendar.test.mjs', ['--experimental-strip-types', '--no-warnings'], 23], ['test/debt-visibility.test.mjs', [], 1], - ['test/commit-hygiene.test.mjs', ['--experimental-strip-types', '--no-warnings'], 2], + ['test/commit-hygiene.test.mjs', ['--experimental-strip-types', '--no-warnings'], 4], // 判据目录自身的卫生:读文本必须走 test/lib/read.mjs 的具名入口 ['test/criteria-hygiene.test.mjs', [], 5], // 用户管理页(P4c 同批):动作↔服务端调用同名 / 门禁只认严格 admin / diff --git a/client/harmony/.gitignore b/client/harmony/.gitignore index d2ff201..2ff87b2 100644 --- a/client/harmony/.gitignore +++ b/client/harmony/.gitignore @@ -9,4 +9,18 @@ /.clang-format /.clang-tidy **/.test -/.appanalyzer \ No newline at end of file +/.appanalyzer + +# ── AGC 客户端配置:**不入版本库**(pi 2026-09-15 的裁定)── +# +# 这个文件**必须在本地存在才能构建**(`hvigorw` 打包时要读它),所以它天然会被 +# `git add -A` 顺手加回来 —— 光靠这行 ignore 挡不住(**ignore 是方便,判据才是机制**, +# 见 `client/electron/test/commit-hygiene.test.mjs` 里那条"不许有 tracked 的 AGC 形状文件")。 +# +# 它含 `client_secret`/`api_key`(AGC 的 `[!00…]` 信封格式)与**明文**的 +# `client_id`/`app_id`/`code1..4`。本仓的镜像(`docs/GITCODE-MIRROR.md`)是 +# **匿名可 clone 的公开项目** ⇒ 一旦随某次 push 上去,就再也撤不回。 +# +# 新同事怎么开始:把 `agconnect-services.example.json` 复制成这个名字, +# 再从 AGC 控制台/密钥库把真值填回去。 +/entry/src/main/resources/rawfile/agconnect-services.json \ No newline at end of file diff --git a/client/harmony/entry/src/main/resources/rawfile/agconnect-services.example.json b/client/harmony/entry/src/main/resources/rawfile/agconnect-services.example.json new file mode 100644 index 0000000..e5966a2 --- /dev/null +++ b/client/harmony/entry/src/main/resources/rawfile/agconnect-services.example.json @@ -0,0 +1,63 @@ +{ + "agcgw_all": { + "SG": "<值>", + "SG_back": "<值>", + "CN": "<值>", + "CN_back": "<值>", + "RU": "<值>", + "RU_back": "<值>", + "DE": "<值>", + "DE_back": "<值>" + }, + "websocketgw_all": { + "SG": "<值>", + "SG_back": "<值>", + "CN": "<值>", + "CN_back": "<值>", + "RU": "<值>", + "RU_back": "<值>", + "DE": "<值>", + "DE_back": "<值>" + }, + "client": { + "cp_id": "<值>", + "product_id": "<值>", + "client_id": "", + "client_secret": "", + "project_id": "<值>", + "app_id": "", + "api_key": "", + "package_name": "com.jianf.agentmail" + }, + "oauth_client": { + "client_id": "", + "client_type": "<值>" + }, + "app_info": { + "app_id": "", + "package_name": "com.jianf.agentmail" + }, + "code": { + "code1": "<值>", + "code2": "<值>", + "code3": "<值>", + "code4": "<值>" + }, + "configuration_version": "<值>", + "appInfos": [ + { + "package_name": "com.jianf.agentmail", + "client": { + "client_secret": "", + "app_id": "", + "api_key": "" + }, + "code": { + "code1": "<值>", + "code2": "<值>", + "code3": "<值>", + "code4": "<值>" + } + } + ] +}