Merge pull request #1037 from bbassie/feature/changelog-stats

Add contributors title and statistics to the changelog script
This commit is contained in:
Cooldude2606 authored and GitHub committed 2026-09-13 18:14:21 +01:00
commit 16f28ec5c7
2 files changed
+73 -5

No files matched your search

+17 -2
View File
@@ -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: {
+56 -3
View File
@@ -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,19 @@ function printMarkdown(
}
}
console.log("### Contributors\n");
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("\n### Statistics\n");
console.log([
`Commits: ${format(statistics.commits)}`,
`Pull Requests: ${format(statistics.pullRequests)}`,
`Files Changed: ${format(statistics.filesChanged)}`,
`Additions: +${format(statistics.lineAdditions)}`,
`Deletions: -${format(statistics.lineDeletions)}`,
].join(" "));
}
async function main() {
@@ -232,17 +284,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}>)`
);