mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Add contributors title and statistics to the changelog script
Puts the contributors under a "Contributors" title on a single comma separated line, and prints commits, merged pull requests, files changed and line additions and deletions since the last release after them. The compare API lists at most 300 files, so the file and line counts come from the diff media type of the same comparison instead. To fetch it, githubFetch now lets the caller override the Accept header and checks the Content-Type against it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a28e2bf9f6
commit
0f4671afd0
2 files changed
+74
-5
No files matched your search
+17
-2
@@ -11,8 +11,8 @@ export async function githubFetch(
|
||||
init = {
|
||||
...init,
|
||||
headers: {
|
||||
...init.headers as Record<string, string> ?? {},
|
||||
"Accept": "application/vnd.github+json",
|
||||
...init.headers as Record<string, string> ?? {},
|
||||
}
|
||||
};
|
||||
if (process.env.GH_TOKEN) {
|
||||
@@ -24,7 +24,9 @@ export async function githubFetch(
|
||||
console.log(response.headers);
|
||||
throw new Error(`GitHub replied with ${response.status} ${response.statusText}: ${await response.text()}`);
|
||||
}
|
||||
if (!response.headers.get("Content-Type")?.startsWith("application/json")) {
|
||||
const accept = init.headers!["Accept"];
|
||||
const contentType = accept === "application/vnd.github+json" ? "application/json" : accept;
|
||||
if (!response.headers.get("Content-Type")?.startsWith(contentType)) {
|
||||
throw new Error(
|
||||
`GitHub replied with ${response.status} ${response.statusText}: ${response.headers.get("Content-Type")}`
|
||||
);
|
||||
@@ -41,6 +43,15 @@ export async function githubFetchJson<T>(
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function githubFetchText(
|
||||
path: string,
|
||||
query: Record<string, string> = {},
|
||||
init: RequestInit & { headers?: Record<string, string> } = {},
|
||||
): Promise<string> {
|
||||
const response = await githubFetch(path, query, init);
|
||||
return await response.text();
|
||||
}
|
||||
|
||||
export async function githubFetchJsonPaginated<T>(
|
||||
path: string,
|
||||
query: Record<string, string> = {},
|
||||
@@ -117,6 +128,10 @@ export interface Release {
|
||||
updated_at?: string | null,
|
||||
}
|
||||
|
||||
export interface Comparison {
|
||||
total_commits: number,
|
||||
}
|
||||
|
||||
export interface Commit {
|
||||
sha: string,
|
||||
commit: {
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import {
|
||||
type Comparison,
|
||||
type Release,
|
||||
type Issue,
|
||||
githubFetchJson,
|
||||
githubFetchJsonPaginated,
|
||||
githubFetchText,
|
||||
} from "./github_api.mts";
|
||||
|
||||
type Changelog = Record<string, string[]>;
|
||||
|
||||
type Statistics = {
|
||||
commits: number,
|
||||
pullRequests: number,
|
||||
filesChanged: number,
|
||||
lineAdditions: number,
|
||||
lineDeletions: number,
|
||||
};
|
||||
|
||||
const repository = "clusterio/clusterio";
|
||||
const branch = "master";
|
||||
|
||||
@@ -187,10 +197,41 @@ async function fetchIssuesUpdatedSince(since: string) {
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchStatistics(lastRelease: Release, pullRequests: Issue[]): Promise<Statistics> {
|
||||
const path = `/repos/${repository}/compare/${lastRelease.tag_name}...${branch}`;
|
||||
const comparison = await githubFetchJson<Comparison>(path, { per_page: "1" });
|
||||
// The JSON response lists at most 300 files, the diff has all of them.
|
||||
const diff = await githubFetchText(path, {}, { headers: { "Accept": "application/vnd.github.diff" } });
|
||||
let filesChanged = 0;
|
||||
let lineAdditions = 0;
|
||||
let lineDeletions = 0;
|
||||
let inHunk = false; // Skips the ---/+++ file headers
|
||||
for (const line of diff.split("\n")) {
|
||||
if (line.startsWith("diff --git ")) {
|
||||
filesChanged += 1;
|
||||
inHunk = false;
|
||||
} else if (line.startsWith("@@")) {
|
||||
inHunk = true;
|
||||
} else if (inHunk && line.startsWith("+")) {
|
||||
lineAdditions += 1;
|
||||
} else if (inHunk && line.startsWith("-")) {
|
||||
lineDeletions += 1;
|
||||
}
|
||||
}
|
||||
return {
|
||||
commits: comparison.total_commits,
|
||||
pullRequests: pullRequests.length,
|
||||
filesChanged,
|
||||
lineAdditions,
|
||||
lineDeletions,
|
||||
};
|
||||
}
|
||||
|
||||
function printMarkdown(
|
||||
changelog: Changelog,
|
||||
issues: Issue[],
|
||||
users: Issue["user"][],
|
||||
statistics: Statistics,
|
||||
refText: (issue: Issue) => string,
|
||||
userText: (user: Issue["user"]) => string,
|
||||
) {
|
||||
@@ -216,8 +257,20 @@ function printMarkdown(
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Contributors");
|
||||
console.log();
|
||||
console.log(`Many thanks to the following for contributing to this release:`);
|
||||
console.log(users.map(userText).join("\n"))
|
||||
console.log(users.map(userText).join(", "));
|
||||
|
||||
const format = (value: number) => value.toLocaleString("en-US");
|
||||
console.log();
|
||||
console.log("Statistics");
|
||||
console.log();
|
||||
console.log(`Commits: ${format(statistics.commits)}`);
|
||||
console.log(`Pull Requests: ${format(statistics.pullRequests)}`);
|
||||
console.log(`Files Changed: ${format(statistics.filesChanged)}`);
|
||||
console.log(`Line Additions: ${format(statistics.lineAdditions)}`);
|
||||
console.log(`Line Deletions: ${format(statistics.lineDeletions)}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@@ -232,17 +285,18 @@ async function main() {
|
||||
|
||||
const changelog = changelogFromPullRequests(pullRequests, issues);
|
||||
const users = [...new Map(pullRequests.map(issue => [issue.user.login, issue.user])).values()]
|
||||
const statistics = await fetchStatistics(lastRelease, pullRequests);
|
||||
|
||||
console.log();
|
||||
console.log("=== Github Release Markdown ===");
|
||||
printMarkdown(changelog, issues, users,
|
||||
printMarkdown(changelog, issues, users, statistics,
|
||||
issue => `${repository}#${issue.number}`,
|
||||
user => `@${user.login}`,
|
||||
);
|
||||
|
||||
console.log();
|
||||
console.log("=== Discord / Changelog Markdown ===");
|
||||
printMarkdown(changelog, issues, users,
|
||||
printMarkdown(changelog, issues, users, statistics,
|
||||
issue => `[#${issue.number}](<${issue.html_url}>)`,
|
||||
user => `[@${user.login}](<${user.html_url}>)`
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user