Files
DeepSeek-Reasonix/scripts/studio-release-notes.mjs
T
esengine 11920eb387 ci(release-studio): credit pull request authors and issue fixers in published notes
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.
2026-09-26 23:28:25 -07:00

51 lines
2.0 KiB
JavaScript

#!/usr/bin/env node
// Renders release-notes/studio/<semver>.md for publication: each #N gains its
// pull request author, or for an issue the pull requests that closed it, and a
// 贡献者 list closes the notes. The source file is never rewritten.
import { readFile, writeFile } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { annotateMarkdown, markdownRefs } from "./markdown-refs.mjs";
import {
contributorLogins,
creditSuffix,
githubRefLookup,
reportWarnings,
resolveCredits,
tokenFromEnvironment,
unresolvedError,
} from "./release-credits.mjs";
export function renderStudioNotes(markdown, credits) {
const body = annotateMarkdown(markdown, (ref) => creditSuffix(credits.get(ref))).trimEnd();
const contributors = contributorLogins(markdownRefs(markdown), credits);
if (!contributors.length) return `${body}\n`;
return `${body}\n\n## 贡献者\n\n感谢本版本的贡献者:${contributors.map((login) => `@${login}`).join("、")}\n`;
}
async function main() {
const [input, output] = process.argv.slice(2);
if (!input || !output) throw new Error("usage: studio-release-notes.mjs <notes.md> <output.md>");
const markdown = await readFile(input, "utf8");
const refs = markdownRefs(markdown);
let credits = new Map();
let failures = [];
if (refs.length) {
const lookup = githubRefLookup({ token: tokenFromEnvironment() });
let warnings;
({ credits, failures, warnings } = await resolveCredits(refs, lookup));
reportWarnings(warnings);
}
await writeFile(output, renderStudioNotes(markdown, credits));
if (failures.length) throw unresolvedError(failures);
console.log(`Rendered ${input} to ${output}: ${new Set(refs).size} reference(s), ${credits.size} resolved`);
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main().catch((error) => {
console.error(error.code ? `${error.code}: ${error.message}` : error.message);
process.exitCode = 1;
});
}