parent
51fe4e5a29
commit
a520c819d1
@ -17,10 +17,10 @@ import {
|
||||
} from "#types/changelogTypes.ts";
|
||||
import dedent from "dedent-js";
|
||||
import mustache from "mustache";
|
||||
import { modChangesAllocations, repoLink } from "./definitions.ts";
|
||||
import { modChangesAllocations } from "./definitions.ts";
|
||||
import ChangelogData from "./changelogData.ts";
|
||||
import { SpecialChangelogFormatting } from "#types/changelogTypes.ts";
|
||||
import { sortCommitListReverse } from "./pusher.ts";
|
||||
import { formatMessage, sortCommitListReverse } from "./pusher.ts";
|
||||
import { logError } from "#utils/log.ts";
|
||||
|
||||
/**
|
||||
@ -30,32 +30,11 @@ const getModChangesFormatting: (
|
||||
commits?: Commit[],
|
||||
) => SpecialChangelogFormatting<Commit[] | undefined> = (commits) => {
|
||||
return {
|
||||
formatting: (message, subMessage, indentation, commits) => {
|
||||
formatting: async (message, subMessage, indentation, commits) => {
|
||||
// Sub messages are details, so make them bold & italic
|
||||
if (subMessage) return `${indentation}* ***${message}***`;
|
||||
|
||||
// Edge Case
|
||||
if (!commits) return `${indentation}* ${message}`;
|
||||
|
||||
if (commits.length > 1) {
|
||||
const authors: string[] = [];
|
||||
const formattedCommits: string[] = [];
|
||||
commits.forEach((commit) => {
|
||||
if (!authors.includes(commit.author_name))
|
||||
authors.push(commit.author_name);
|
||||
formattedCommits.push(
|
||||
`[\`${commit.hash.substring(0, 7)}\`](${repoLink}commit/${commit.hash})`,
|
||||
);
|
||||
});
|
||||
authors.sort();
|
||||
return `${indentation}* ${message} - **${authors.join("**, **")}** (${formattedCommits.join(", ")})`;
|
||||
}
|
||||
|
||||
const commit = commits[0];
|
||||
const shortSHA = commit.hash.substring(0, 7);
|
||||
const author = commit.author_name;
|
||||
|
||||
return `${indentation}* ${message} - **${author}** ([\`${shortSHA}\`](${repoLink}commit/${commit.hash}))`;
|
||||
return formatMessage(message, indentation, commits, subMessage);
|
||||
},
|
||||
storage: commits,
|
||||
} as SpecialChangelogFormatting<Commit[] | undefined>;
|
||||
|
@ -3,7 +3,12 @@ import { categories, defaultIndentation } from "./definitions.ts";
|
||||
import { Category, ChangelogMessage, Commit } from "#types/changelogTypes.ts";
|
||||
import { repoLink } from "./definitions.ts";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { getIssueURL, getNewestIssueURLs } from "#utils/util.ts";
|
||||
import {
|
||||
formatAuthor,
|
||||
getIssueURL,
|
||||
getNewestCommitAuthors,
|
||||
getNewestIssueURLs,
|
||||
} from "#utils/util.ts";
|
||||
|
||||
let data: ChangelogData;
|
||||
let octokit: Octokit;
|
||||
@ -14,6 +19,9 @@ const sectionLinesBeforeCommitLogExcluded = 50;
|
||||
// How many lines the commit log can be before its is excluded.
|
||||
const logLinesBeforeCommitLogExcluded = 20;
|
||||
|
||||
// How many commits to include after a message.
|
||||
const maxIncludeCommits = 3;
|
||||
|
||||
export default async function pushAll(inputData: ChangelogData): Promise<void> {
|
||||
pushTitle(inputData);
|
||||
await pushChangelog(inputData);
|
||||
@ -75,12 +83,14 @@ export async function pushChangelog(inputData: ChangelogData): Promise<void> {
|
||||
data.builder.length < sectionLinesBeforeCommitLogExcluded &&
|
||||
data.commitList.length < logLinesBeforeCommitLogExcluded
|
||||
) {
|
||||
// Commit List is relatively short, and most commits would have been handled via category pushing anyway.
|
||||
// Just retrieve each author info sequentially.
|
||||
sortCommitList(data.commitList, (commit) => commit);
|
||||
|
||||
data.builder.push("## Commits");
|
||||
data.commitList.forEach((commit) => {
|
||||
data.builder.push(formatCommit(commit));
|
||||
});
|
||||
for (const commit of data.commitList) {
|
||||
data.builder.push(await formatCommit(commit));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No Commit List = No Changes
|
||||
@ -118,19 +128,31 @@ async function pushCategory(category: Category) {
|
||||
categoryLog.push(`### ${subCategory.keyName}:`);
|
||||
}
|
||||
|
||||
// Format Main Messages (Async so Author Fetch is Fast)
|
||||
await getNewestCommitAuthors(octokit);
|
||||
const formatted: { message: ChangelogMessage; formatted: string }[] =
|
||||
await Promise.all(
|
||||
list.map((message) =>
|
||||
formatChangelogMessage(message).then((formatted) => {
|
||||
return { message, formatted };
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
// Sort Log
|
||||
sortCommitList(
|
||||
list,
|
||||
(message) => message.commitObject,
|
||||
(a, b) => a.commitMessage.localeCompare(b.commitMessage),
|
||||
formatted,
|
||||
(formatted) => formatted.message.commitObject,
|
||||
(a, b) =>
|
||||
a.message.commitMessage.localeCompare(b.message.commitMessage),
|
||||
);
|
||||
|
||||
// Push Log
|
||||
for (const changelogMessage of list) {
|
||||
categoryLog.push(await formatChangelogMessage(changelogMessage));
|
||||
// Push Sub Messages
|
||||
if (changelogMessage.subChangelogMessages) {
|
||||
for (const subMessage of changelogMessage.subChangelogMessages)
|
||||
for (const format of formatted) {
|
||||
categoryLog.push(format.formatted);
|
||||
// Push Sub Messages (No need for Async, Author Info Not Calculated in Sub Messages)
|
||||
if (format.message.subChangelogMessages) {
|
||||
for (const subMessage of format.message.subChangelogMessages)
|
||||
categoryLog.push(await formatChangelogMessage(subMessage, true));
|
||||
}
|
||||
}
|
||||
@ -222,58 +244,110 @@ async function formatChangelogMessage(
|
||||
changelogMessage.specialFormatting.storage,
|
||||
);
|
||||
|
||||
if (changelogMessage.commitObject && !subMessage) {
|
||||
if (data.combineList.has(changelogMessage.commitObject.hash)) {
|
||||
const commits =
|
||||
data.combineList.get(changelogMessage.commitObject.hash) ?? [];
|
||||
commits.push(changelogMessage.commitObject);
|
||||
|
||||
// Sort original array so newest commits appear at the end instead of start of commit string
|
||||
sortCommitListReverse(commits);
|
||||
|
||||
const formattedCommits: string[] = [];
|
||||
const authors: string[] = [];
|
||||
const authorEmails: Set<string> = new Set<string>();
|
||||
const processedSHAs: Set<string> = new Set<string>();
|
||||
|
||||
commits.forEach((commit) => {
|
||||
if (processedSHAs.has(commit.hash)) return;
|
||||
if (
|
||||
!authors.includes(commit.author_name) &&
|
||||
!authorEmails.has(commit.author_email)
|
||||
) {
|
||||
authors.push(commit.author_name);
|
||||
authorEmails.add(commit.author_email);
|
||||
}
|
||||
formattedCommits.push(
|
||||
`[\`${commit.hash.substring(0, 7)}\`](${repoLink}commit/${commit.hash})`,
|
||||
);
|
||||
processedSHAs.add(commit.hash);
|
||||
});
|
||||
|
||||
authors.sort();
|
||||
return `${indentation}* ${message} - **${authors.join("**, **")}** (${formattedCommits.join(", ")})`;
|
||||
}
|
||||
const commit = changelogMessage.commitObject;
|
||||
const shortSHA = commit.hash.substring(0, 7);
|
||||
const author = commit.author_name;
|
||||
|
||||
return `${indentation}* ${message} - **${author}** ([\`${shortSHA}\`](${repoLink}commit/${commit.hash}))`;
|
||||
if (!changelogMessage.commitObject || subMessage) {
|
||||
return formatMessage(message, indentation, undefined, subMessage);
|
||||
}
|
||||
|
||||
return `${indentation}* ${message}`;
|
||||
if (data.combineList.has(changelogMessage.commitObject.hash)) {
|
||||
const commits =
|
||||
data.combineList.get(changelogMessage.commitObject.hash) ?? [];
|
||||
commits.push(changelogMessage.commitObject);
|
||||
|
||||
return formatMessage(message, indentation, commits, subMessage);
|
||||
}
|
||||
|
||||
return formatMessage(
|
||||
message,
|
||||
indentation,
|
||||
[changelogMessage.commitObject],
|
||||
subMessage,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a Changelog Message
|
||||
* @param message The message to format.
|
||||
* @param indentation Indentation to use.
|
||||
* @param commits List of Commits
|
||||
* @param subMessage Whether this message is a subMessage (used in details). Set to true to make it a subMessage (different parsing). Defaults to false.
|
||||
* @return string Formatted Changelog Message
|
||||
*/
|
||||
export async function formatMessage(
|
||||
message: string,
|
||||
indentation: string,
|
||||
commits?: Commit[],
|
||||
subMessage = false,
|
||||
): Promise<string> {
|
||||
if (!commits || commits.length == 0 || subMessage) {
|
||||
return `${indentation}* ${message}`;
|
||||
}
|
||||
|
||||
if (commits.length === 1) {
|
||||
const commit = commits[0];
|
||||
const shortSHA = commit.hash.substring(0, 7);
|
||||
const formattedCommit = `[\`${shortSHA}\`](${repoLink}commit/${commit.hash})`;
|
||||
const author = await formatAuthor(commit, octokit);
|
||||
|
||||
return `${indentation}* ${message} - ${author} (${formattedCommit})`;
|
||||
}
|
||||
|
||||
// Sort original array so newest commits appear at the end instead of start of commit string
|
||||
sortCommitListReverse(commits);
|
||||
|
||||
const formattedCommits: string[] = [];
|
||||
const authors: string[] = [];
|
||||
const retrievedAuthors: { commit: Commit; formatted: string }[] =
|
||||
await Promise.all(
|
||||
commits.map((commit) =>
|
||||
formatAuthor(commit, octokit).then((formatted) => {
|
||||
return { commit, formatted };
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const processedAuthors: Set<string> = new Set<string>();
|
||||
const processedEmails: Set<string> = new Set<string>();
|
||||
const processedSHAs: Set<string> = new Set<string>();
|
||||
|
||||
sortCommitList(
|
||||
retrievedAuthors,
|
||||
(author) => author.commit,
|
||||
(a, b) => a.formatted.localeCompare(b.formatted),
|
||||
);
|
||||
retrievedAuthors.forEach((pAuthor) => {
|
||||
if (processedSHAs.has(pAuthor.commit.hash)) return;
|
||||
if (
|
||||
!processedAuthors.has(pAuthor.formatted) &&
|
||||
!processedEmails.has(pAuthor.commit.author_email)
|
||||
) {
|
||||
authors.push(pAuthor.formatted);
|
||||
processedAuthors.add(pAuthor.formatted);
|
||||
processedEmails.add(pAuthor.commit.author_email);
|
||||
}
|
||||
formattedCommits.push(
|
||||
`[\`${pAuthor.commit.hash.substring(0, 7)}\`](${repoLink}commit/${pAuthor.commit.hash})`,
|
||||
);
|
||||
processedSHAs.add(pAuthor.commit.hash);
|
||||
});
|
||||
|
||||
// Delete all Formatted Commits after MaxIncludeCommits elements, replace with '...'
|
||||
if (formattedCommits.length > maxIncludeCommits) {
|
||||
formattedCommits.splice(maxIncludeCommits, Infinity, "...");
|
||||
}
|
||||
|
||||
return `${indentation}* ${message} - ${authors.join(", ")} (${formattedCommits.join(", ")})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted commit
|
||||
*/
|
||||
function formatCommit(commit: Commit): string {
|
||||
async function formatCommit(commit: Commit): Promise<string> {
|
||||
const date = new Date(commit.date).toLocaleDateString("en-us", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
const formattedCommit = `${commit.message} - **${commit.author_name}** (${date})`;
|
||||
const formattedCommit = `${commit.message} - ${await formatAuthor(commit, octokit)} (${date})`;
|
||||
|
||||
const shortSHA = commit.hash.substring(0, 7);
|
||||
|
||||
|
@ -134,7 +134,7 @@ export interface SpecialChangelogFormatting<T> {
|
||||
subMessage: boolean,
|
||||
indentation: string,
|
||||
storage?: T,
|
||||
) => string;
|
||||
) => Promise<string>;
|
||||
|
||||
/**
|
||||
* Storage
|
||||
|
@ -593,9 +593,15 @@ export async function getVersionManifest(
|
||||
*/
|
||||
export function cleanupVersion(version?: string): string {
|
||||
if (!version) return "";
|
||||
|
||||
if (!version.replace(/[\d+.?]+/g, "")) return version;
|
||||
|
||||
version = version.replace(/1\.12\.2|1\.12|\.jar/g, "");
|
||||
const list = version.match(/[\d+.?]+/g);
|
||||
if (!list) return version;
|
||||
|
||||
if (list[list.length - 1] == "0") return version;
|
||||
|
||||
return list[list.length - 1];
|
||||
}
|
||||
|
||||
@ -650,20 +656,99 @@ export async function getIssueURL(
|
||||
logError(
|
||||
`Failed to get the Issue/PR Info for Issue/PR #${issueNumber}. Returned Status Code ${issueInfo.status}, expected Status 200.`,
|
||||
);
|
||||
issueURLCache.set(issueNumber, "");
|
||||
return "";
|
||||
}
|
||||
logInfo(
|
||||
`No Issue URL Cache for Issue Number ${issueNumber}. Retrieved Specifically.`,
|
||||
);
|
||||
issueURLCache.set(issueNumber, issueInfo.data.html_url);
|
||||
return issueInfo.data.html_url;
|
||||
} catch (e) {
|
||||
logError(
|
||||
`Failed to get the Issue/PR Info for Issue/PR #${issueNumber}. This may be because this is not a PR or Issue, or could be because of rate limits.`,
|
||||
);
|
||||
issueURLCache.set(issueNumber, "");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// Map of Commit SHA -> Formatted Author
|
||||
const commitAuthorCache: Map<string, string> = new Map<string, string>();
|
||||
|
||||
/**
|
||||
* Fills the Commit Author Cache with the newest 100 commits from the repo.
|
||||
*/
|
||||
export async function getNewestCommitAuthors(octokit: Octokit): Promise<void> {
|
||||
if (commitAuthorCache.size > 0) return;
|
||||
try {
|
||||
const commits = await octokit.repos.listCommits({
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
per_page: 100,
|
||||
});
|
||||
if (commits.status !== 200) {
|
||||
logError(
|
||||
`Failed to get all Commit Authors. Returned Status Code ${commits.status}, expected Status 200.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
commits.data.forEach((commit) => {
|
||||
if (!commitAuthorCache.has(commit.sha))
|
||||
commitAuthorCache.set(commit.sha, commit.author?.login ?? "");
|
||||
});
|
||||
} catch (e) {
|
||||
logError(
|
||||
"Failed to get all Commit Authors of Repo. This may be because there are no commits, or because of rate limits.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Author, in mentionable form (@login), or default (**Display Name**), from a Commit.
|
||||
*/
|
||||
export async function formatAuthor(commit: Commit, octokit: Octokit) {
|
||||
const defaultFormat = `**${commit.author_name}**`;
|
||||
|
||||
if (commitAuthorCache.has(commit.hash)) {
|
||||
const login = commitAuthorCache.get(commit.hash);
|
||||
if (login) return `@${login}`;
|
||||
return defaultFormat;
|
||||
}
|
||||
|
||||
try {
|
||||
const commitInfo = await octokit.repos.getCommit({
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
ref: commit.hash,
|
||||
});
|
||||
if (commitInfo.status !== 200) {
|
||||
logError(
|
||||
`Failed to get the Author Info for Commit ${commit.hash}. Returned Status Code ${commitInfo.status}, expected Status 200.`,
|
||||
);
|
||||
commitAuthorCache.set(commit.hash, "");
|
||||
return defaultFormat;
|
||||
}
|
||||
if (!commitInfo.data.author?.login) {
|
||||
logError(
|
||||
`Failed to get the Author Info for Commit ${commit.hash}. Returned Null Data, Author or Login.`,
|
||||
);
|
||||
commitAuthorCache.set(commit.hash, "");
|
||||
return defaultFormat;
|
||||
}
|
||||
logInfo(
|
||||
`No Author Cache for Commit ${commit.hash}. Retrieved Specifically.`,
|
||||
);
|
||||
return `@${commitInfo.data.author.login}`;
|
||||
} catch (e) {
|
||||
logError(
|
||||
`Failed to get Commit Author for Commit ${commit.hash}. This may be because there are no commits, or because of rate limits.`,
|
||||
);
|
||||
commitAuthorCache.set(commit.hash, "");
|
||||
return defaultFormat;
|
||||
}
|
||||
}
|
||||
|
||||
export const FORGE_VERSION_REG = /forge-(.+)/;
|
||||
export const FORGE_MAVEN = "https://files.minecraftforge.net/maven/";
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user