Skip to content

Commit 34c7c35

Browse files
committed
fix(ci): 修好两处只在 CI 才会红的门禁(都不是功能 bug,但都该本地可复现)
generated-drift:两个生成器用 `git rev-parse --short HEAD` 写 sourceCommit, 而短哈希长度取决于**该 clone 的对象数量**——CI 的全新检出给 7 位、开发者的 clone 给 8 位, 于是同一份 mcpp 生成出两个不同文件,门禁在 API 毫无变化时变红(本次就是如此,内容完全一致)。 改成写完整 40 位哈希,并让 check-generators 只比较**数据**:sourceVersion/sourceCommit 以及由 commit 拼出的文档链接属于"出处",每次上游提交都会动,把它们当漂移等于门禁永远红。 package:`! grep -rqs "vscode-languageclient" dist/src` 会被"解释为什么不用语言客户端"的 **编译后注释**命中。改成匹配真正的模块说明符(require/from)。 两处都补了本地门禁,免得下次只能等 CI: - 快照的 sourceCommit 必须是 40 位十六进制(正是今天这个 bug 的类别); - src 里不得 import/require vscode-languageclient(CI 那条 grep 的本意)。 662 测试通过;四道门禁全过。
1 parent 7a7dd44 commit 34c7c35

7 files changed

Lines changed: 95 additions & 13 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ jobs:
117117
- name: No second language client in the build output
118118
run: |
119119
test ! -e dist/src/languageClient.js
120-
! grep -rqs "vscode-languageclient" dist/src package.json
120+
# A *dependency*, not a mention. `dist` carries the compiled comments
121+
# that explain why this extension does not drive a language client, and
122+
# grepping for the bare name failed the build on those comments alone.
123+
! grep -rqsE "require\(['\"]vscode-languageclient['\"]\)|from ['\"]vscode-languageclient['\"]" dist/src package.json
121124
- uses: actions/upload-artifact@v4
122125
with:
123126
name: vsix

‎data/buildscript-api.json‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sourceVersion": "2026.10.3.1",
3-
"sourceCommit": "74bcb859",
3+
"sourceCommit": "74bcb859e60afc71ad759540d2de5344dda400e9",
44
"protocolVersion": 15,
55
"cacheEpoch": 3,
66
"roles": [
@@ -131,7 +131,7 @@
131131
"mustExist": false,
132132
"since": 4,
133133
"rules": [],
134-
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859/docs/30-build-mcpp.md#runner-how-the-artifact-is-executed-20268192"
134+
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859e60afc71ad759540d2de5344dda400e9/docs/30-build-mcpp.md#runner-how-the-artifact-is-executed-20268192"
135135
},
136136
{
137137
"wire": "runner-named",
@@ -262,7 +262,7 @@
262262
"R2.1",
263263
"R9.4"
264264
],
265-
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859/docs/30-build-mcpp.md#warning-succeeding-and-still-being-heard-20268212"
265+
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859e60afc71ad759540d2de5344dda400e9/docs/30-build-mcpp.md#warning-succeeding-and-still-being-heard-20268212"
266266
},
267267
{
268268
"wire": "diagnostic",
@@ -312,7 +312,7 @@
312312
"rules": [
313313
"R2.1"
314314
],
315-
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859/docs/30-build-mcpp.md#the-probe-channel-fact-floor-2026952"
315+
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859e60afc71ad759540d2de5344dda400e9/docs/30-build-mcpp.md#the-probe-channel-fact-floor-2026952"
316316
},
317317
{
318318
"wire": "floor",
@@ -325,7 +325,7 @@
325325
"rules": [
326326
"R2.1"
327327
],
328-
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859/docs/30-build-mcpp.md#the-probe-channel-fact-floor-2026952"
328+
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859e60afc71ad759540d2de5344dda400e9/docs/30-build-mcpp.md#the-probe-channel-fact-floor-2026952"
329329
},
330330
{
331331
"wire": "pack-format",
@@ -374,7 +374,7 @@
374374
"R9.2",
375375
"R9.5"
376376
],
377-
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859/docs/30-build-mcpp.md#deploying-what-the-program-generated-deploy-20269123-protocol-11"
377+
"docsUrl": "https://github.com/mcpp-community/mcpp/blob/74bcb859e60afc71ad759540d2de5344dda400e9/docs/30-build-mcpp.md#deploying-what-the-program-generated-deploy-20269123-protocol-11"
378378
},
379379
{
380380
"wire": "runtime-search-dir",

‎data/toml-schema.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sourceVersion": "2026.10.3.1",
3-
"sourceCommit": "74bcb859",
3+
"sourceCommit": "74bcb859e60afc71ad759540d2de5344dda400e9",
44
"sections": [
55
{
66
"header": "[build]",

‎test/artifacts.test.ts‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,38 @@ test("the library view cannot reload itself in a loop", () => {
222222
assert.match(view, /this\.document = undefined;/);
223223
});
224224

225+
test("no module drives a second language client", () => {
226+
// The same rule the `package` job enforces, but runnable here: the CI grep used
227+
// to match the bare name, and the compiled comments that explain why this
228+
// extension does *not* use a language client made it fail on a clean build.
229+
const offenders: string[] = [];
230+
for (const [file, text] of sourceFiles()) {
231+
const code = text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
232+
if (/from\s+["']vscode-languageclient["']|require\(["']vscode-languageclient["']\)/.test(code)) {
233+
offenders.push(file);
234+
}
235+
}
236+
assert.deepEqual(offenders, [], "a language client is imported again");
237+
});
238+
239+
test("the committed snapshots record a clone-independent commit", () => {
240+
// `git rev-parse --short` shortens to the shortest unique prefix *for that
241+
// clone*, so the same mcpp produced a 7-character hash in a fresh CI checkout
242+
// and 8 in a developer's — and the drift gate went red on an unchanged API.
243+
for (const file of ["data/buildscript-api.json", "data/toml-schema.json"]) {
244+
const snapshot = JSON.parse(readFileSync(path.join(root, file), "utf8")) as {
245+
sourceVersion?: string;
246+
sourceCommit?: string;
247+
};
248+
assert.match(snapshot.sourceVersion ?? "", /^\d{4}\.\d+\.\d+/, `${file}: no mcpp version`);
249+
assert.match(
250+
snapshot.sourceCommit ?? "",
251+
/^[0-9a-f]{40}$/,
252+
`${file}: the provenance hash must be full length, not an abbreviation`,
253+
);
254+
}
255+
});
256+
225257
test("opening a link always produces an answer", () => {
226258
// The comment above `openExternal` quotes the old call, so comments are
227259
// stripped before the check — the point is the code, not the prose.

‎tools/check-generators.mjs‎

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
* checkout present. That only stays honest if something notices when the source
88
* moves, so CI regenerates them here and fails on any difference.
99
*
10+
* **What "difference" means: the data, not the provenance.** The two files record
11+
* which mcpp they came from (`sourceVersion`, `sourceCommit`, and doc links built
12+
* from the commit). Every mcpp commit moves those, so comparing them literally
13+
* made this gate red for reasons no one in this repository caused — and a gate
14+
* that is always red is not a gate. `canonical()` below blanks that provenance and
15+
* compares the rest, so a red run means the API or the schema really moved.
16+
*
1017
* Without a checkout (`MCPP_REPO`, else `../mcpp`) this **skips with a notice**
1118
* rather than passing silently: a green run that checked nothing is worse than a
1219
* yellow one.
@@ -29,6 +36,35 @@ const targets = [
2936
{ name: "mcpp.toml schema", script: "tools/generate-toml-schema.mjs", file: "data/toml-schema.json" },
3037
];
3138

39+
/**
40+
* One JSON document with its **provenance** replaced by placeholders.
41+
*
42+
* `sourceVersion` and `sourceCommit` are facts about the checkout the snapshot was
43+
* generated from, and any string containing the commit (the documentation links)
44+
* is derived from it. Two clones of the same mcpp agree on everything else.
45+
*/
46+
function canonical(text) {
47+
const value = JSON.parse(text);
48+
const commit = typeof value.sourceCommit === "string" ? value.sourceCommit : "";
49+
const scrub = (node) => {
50+
if (typeof node === "string") {
51+
return commit.length === 0 ? node : node.split(commit).join("<commit>");
52+
}
53+
if (Array.isArray(node)) {
54+
return node.map(scrub);
55+
}
56+
if (node !== null && typeof node === "object") {
57+
const out = {};
58+
for (const [key, child] of Object.entries(node)) {
59+
out[key] = key === "sourceVersion" || key === "sourceCommit" ? "<provenance>" : scrub(child);
60+
}
61+
return out;
62+
}
63+
return node;
64+
};
65+
return JSON.stringify(scrub(value));
66+
}
67+
3268
let drifted = 0;
3369
for (const target of targets) {
3470
const before = fs.readFileSync(path.join(root, target.file), "utf8");
@@ -41,8 +77,11 @@ for (const target of targets) {
4177
continue;
4278
}
4379
const after = fs.readFileSync(path.join(root, target.file), "utf8");
44-
if (before !== after) {
45-
console.error(`error: ${target.file} is out of date — run \`npm run gen:…\` and commit the result`);
80+
if (canonical(before) !== canonical(after)) {
81+
console.error(
82+
`error: ${target.file} is out of date — the mcpp it was generated from now answers differently; ` +
83+
`run \`npm run gen:…\` and commit the result`,
84+
);
4685
drifted += 1;
4786
} else {
4887
console.log(`check-generators: ${target.name} is current`);

‎tools/generate-buildscript-api.mjs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ function parseSourceVersion(text) {
107107

108108
function sourceCommit() {
109109
try {
110-
return execFileSync("git", ["rev-parse", "--short", "HEAD"], {
110+
// The **full** hash, never `--short`: git shortens to the shortest unique
111+
// prefix *for this clone*, so a fresh CI checkout (7 characters) and a
112+
// developer's clone with more objects (8) produced two different files for
113+
// the same mcpp — the drift gate went red on an unchanged API.
114+
return execFileSync("git", ["rev-parse", "HEAD"], {
111115
cwd: mcppRepo,
112116
encoding: "utf8",
113117
stdio: ["ignore", "pipe", "ignore"],

‎tools/generate-toml-schema.mjs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ function readPackageVersion(tomlText) {
9696

9797
function readSourceCommit() {
9898
try {
99-
return execFileSync("git", ["rev-parse", "--short", "HEAD"], {
99+
// The **full** hash, never `--short`: git shortens to the shortest unique
100+
// prefix *for this clone*, so a fresh CI checkout (7 characters) and a
101+
// developer's clone with more objects (8) produced two different files for
102+
// the same mcpp — the drift gate went red on an unchanged API.
103+
return execFileSync("git", ["rev-parse", "HEAD"], {
100104
cwd: MCPP_REPO,
101105
encoding: "utf8",
102106
stdio: ["ignore", "pipe", "ignore"],
@@ -541,7 +545,7 @@ function main() {
541545
const keys = sections.reduce((total, section) => total + (section.keys?.length ?? 0), 0);
542546
process.stdout.write(
543547
`generate-toml-schema: wrote ${OUT_FILE}\n` +
544-
` mcpp ${version} (${schema.sourceCommit}), ${sections.length} sections, ${keys} keys, ` +
548+
` mcpp ${version} (${schema.sourceCommit.slice(0, 8)}), ${sections.length} sections, ${keys} keys, ` +
545549
`${sections.filter((section) => section.plane === UNCLASSIFIED_PLANE).length} unclassified\n`,
546550
);
547551
}

0 commit comments

Comments
 (0)