mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
The publish job now renders release-notes/studio/<semver>.md before it
signs anything. A #N that is a pull request gains "by @author"; a #N that
is an issue gains "fixed in #M by @author" for each merged pull request
GitHub links as closing it (the ClosedEvent closer and
closedByPullRequestsReferences, read over GraphQL; mentions are not
fixes). An issue fixed by a direct push stays plain. A 贡献者 list of the
credited humans, in first-appearance order, closes the notes. Bot authors
are never credited; the source file is not rewritten.
scripts/markdown-refs.mjs decides what is a reference: #N at a line start
or after whitespace, "(", "(" or a list separator, outside code spans,
backtick and tilde fences (each closed only by its own kind), indented
code, HTML comments and link targets. A number that names a discussion or
nothing stays plain with a warning instead of failing the release.
Lookups retry with backoff on 5xx, rate limits and dropped connections. A
failure that persists, or an authorisation failure, fails the job with a
release_credits.* code before signing. The publish job gains
issues: read and pull-requests: read.
CI gains a release-notes-scripts job, gated on scripts/ and
release-notes/studio/, that runs the three test files with contents: read.
scripts/release-credits.mjs is the same module the 1.x renderer uses.
83 lines
3.0 KiB
JavaScript
83 lines
3.0 KiB
JavaScript
import assert from "node:assert/strict";
|
||
import { readdir, readFile } from "node:fs/promises";
|
||
import { test } from "node:test";
|
||
import { markdownRefs } from "./markdown-refs.mjs";
|
||
import { CreditError, CreditErrorCode, resolveCredits } from "./release-credits.mjs";
|
||
import { renderStudioNotes } from "./studio-release-notes.mjs";
|
||
|
||
const notes = `本版修复两件事。
|
||
|
||
## 修复
|
||
|
||
- 读图模型不再声称看不到图片(#20)(44150b0aa)
|
||
- Windows 窗口可以打开 #21
|
||
- 依赖升级 #22,并补上 #20 的回归测试
|
||
- 会话不再丢失(#23)
|
||
`;
|
||
|
||
const authors = {
|
||
20: { kind: "pull", login: "alice", bot: false },
|
||
21: { kind: "issue", fixes: [] },
|
||
22: { kind: "pull", login: "dependabot[bot]", bot: true },
|
||
23: { kind: "issue", fixes: [{ number: 30, login: "bob", bot: false }] },
|
||
};
|
||
|
||
test("pull requests credit their authors, fixed issues their fixers, the rest stay plain", async () => {
|
||
const calls = [];
|
||
const { credits, failures } = await resolveCredits(markdownRefs(notes), async (ref) => {
|
||
calls.push(ref);
|
||
return authors[ref];
|
||
});
|
||
assert.deepEqual(failures, []);
|
||
assert.deepEqual(calls.sort(), [20, 21, 22, 23]);
|
||
assert.equal(
|
||
renderStudioNotes(notes, credits),
|
||
`本版修复两件事。
|
||
|
||
## 修复
|
||
|
||
- 读图模型不再声称看不到图片(#20 by @alice)(44150b0aa)
|
||
- Windows 窗口可以打开 #21
|
||
- 依赖升级 #22,并补上 #20 by @alice 的回归测试
|
||
- 会话不再丢失(#23 fixed in #30 by @bob)
|
||
|
||
## 贡献者
|
||
|
||
感谢本版本的贡献者:@alice、@bob
|
||
`,
|
||
);
|
||
});
|
||
|
||
test("an unresolved ref stays plain and is reported by code", async () => {
|
||
const { credits, failures } = await resolveCredits(markdownRefs(notes), async (ref) => {
|
||
if (ref === 20) throw new CreditError(CreditErrorCode.unreachable, "#20: down", { ref });
|
||
return authors[ref];
|
||
});
|
||
assert.deepEqual(failures.map((failure) => [failure.ref, failure.code]), [[20, CreditErrorCode.unreachable]]);
|
||
const rendered = renderStudioNotes(notes, credits);
|
||
assert.doesNotMatch(rendered, /#20 by/);
|
||
assert.match(rendered, /#23 fixed in #30 by @bob/);
|
||
assert.match(rendered, /感谢本版本的贡献者:@bob\n$/);
|
||
});
|
||
|
||
test("committed notes render unchanged when nothing is credited", async () => {
|
||
const dir = new URL("../release-notes/studio/", import.meta.url);
|
||
for (const name of await readdir(dir)) {
|
||
const source = await readFile(new URL(name, dir), "utf8");
|
||
assert.equal(renderStudioNotes(source, new Map()), `${source.trimEnd()}\n`, name);
|
||
}
|
||
});
|
||
|
||
test("a number naming a discussion or nothing stays plain and only warns", async () => {
|
||
const source = "- 讨论见 #40,颜色改为 #123456\n";
|
||
const { credits, failures, warnings } = await resolveCredits(markdownRefs(source), async (ref) =>
|
||
ref === 40 ? { kind: "discussion" } : { kind: "missing" },
|
||
);
|
||
assert.deepEqual(failures, []);
|
||
assert.deepEqual(
|
||
warnings.map((warning) => warning.code),
|
||
[CreditErrorCode.discussion, CreditErrorCode.notFound],
|
||
);
|
||
assert.equal(renderStudioNotes(source, credits), source);
|
||
});
|