parent
51fe4e5a29
commit
a520c819d1
@ -17,10 +17,10 @@ import {
|
|||||||
} from "#types/changelogTypes.ts";
|
} from "#types/changelogTypes.ts";
|
||||||
import dedent from "dedent-js";
|
import dedent from "dedent-js";
|
||||||
import mustache from "mustache";
|
import mustache from "mustache";
|
||||||
import { modChangesAllocations, repoLink } from "./definitions.ts";
|
import { modChangesAllocations } from "./definitions.ts";
|
||||||
import ChangelogData from "./changelogData.ts";
|
import ChangelogData from "./changelogData.ts";
|
||||||
import { SpecialChangelogFormatting } from "#types/changelogTypes.ts";
|
import { SpecialChangelogFormatting } from "#types/changelogTypes.ts";
|
||||||
import { sortCommitListReverse } from "./pusher.ts";
|
import { formatMessage, sortCommitListReverse } from "./pusher.ts";
|
||||||
import { logError } from "#utils/log.ts";
|
import { logError } from "#utils/log.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,32 +30,11 @@ const getModChangesFormatting: (
|
|||||||
commits?: Commit[],
|
commits?: Commit[],
|
||||||
) => SpecialChangelogFormatting<Commit[] | undefined> = (commits) => {
|
) => SpecialChangelogFormatting<Commit[] | undefined> = (commits) => {
|
||||||
return {
|
return {
|
||||||
formatting: (message, subMessage, indentation, commits) => {
|
formatting: async (message, subMessage, indentation, commits) => {
|
||||||
// Sub messages are details, so make them bold & italic
|
// Sub messages are details, so make them bold & italic
|
||||||
if (subMessage) return `${indentation}* ***${message}***`;
|
if (subMessage) return `${indentation}* ***${message}***`;
|
||||||
|
|
||||||
// Edge Case
|
return formatMessage(message, indentation, commits, subMessage);
|
||||||
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}))`;
|
|
||||||
},
|
},
|
||||||
storage: commits,
|
storage: commits,
|
||||||
} as SpecialChangelogFormatting<Commit[] | undefined>;
|
} as SpecialChangelogFormatting<Commit[] | undefined>;
|
||||||
|
@ -3,7 +3,12 @@ import { categories, defaultIndentation } from "./definitions.ts";
|
|||||||
import { Category, ChangelogMessage, Commit } from "#types/changelogTypes.ts";
|
import { Category, ChangelogMessage, Commit } from "#types/changelogTypes.ts";
|
||||||
import { repoLink } from "./definitions.ts";
|
import { repoLink } from "./definitions.ts";
|
||||||
import { Octokit } from "@octokit/rest";
|
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 data: ChangelogData;
|
||||||
let octokit: Octokit;
|
let octokit: Octokit;
|
||||||
@ -14,6 +19,9 @@ const sectionLinesBeforeCommitLogExcluded = 50;
|
|||||||
// How many lines the commit log can be before its is excluded.
|
// How many lines the commit log can be before its is excluded.
|
||||||
const logLinesBeforeCommitLogExcluded = 20;
|
const logLinesBeforeCommitLogExcluded = 20;
|
||||||
|
|
||||||
|
// How many commits to include after a message.
|
||||||
|
const maxIncludeCommits = 3;
|
||||||
|
|
||||||
export default async function pushAll(inputData: ChangelogData): Promise<void> {
|
export default async function pushAll(inputData: ChangelogData): Promise<void> {
|
||||||
pushTitle(inputData);
|
pushTitle(inputData);
|
||||||
await pushChangelog(inputData);
|
await pushChangelog(inputData);
|
||||||
@ -75,12 +83,14 @@ export async function pushChangelog(inputData: ChangelogData): Promise<void> {
|
|||||||
data.builder.length < sectionLinesBeforeCommitLogExcluded &&
|
data.builder.length < sectionLinesBeforeCommitLogExcluded &&
|
||||||
data.commitList.length < logLinesBeforeCommitLogExcluded
|
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);
|
sortCommitList(data.commitList, (commit) => commit);
|
||||||
|
|
||||||
data.builder.push("## Commits");
|
data.builder.push("## Commits");
|
||||||
data.commitList.forEach((commit) => {
|
for (const commit of data.commitList) {
|
||||||
data.builder.push(formatCommit(commit));
|
data.builder.push(await formatCommit(commit));
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No Commit List = No Changes
|
// No Commit List = No Changes
|
||||||
@ -118,19 +128,31 @@ async function pushCategory(category: Category) {
|
|||||||
categoryLog.push(`### ${subCategory.keyName}:`);
|
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
|
// Sort Log
|
||||||
sortCommitList(
|
sortCommitList(
|
||||||
list,
|
formatted,
|
||||||
(message) => message.commitObject,
|
(formatted) => formatted.message.commitObject,
|
||||||
(a, b) => a.commitMessage.localeCompare(b.commitMessage),
|
(a, b) =>
|
||||||
|
a.message.commitMessage.localeCompare(b.message.commitMessage),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Push Log
|
// Push Log
|
||||||
for (const changelogMessage of list) {
|
for (const format of formatted) {
|
||||||
categoryLog.push(await formatChangelogMessage(changelogMessage));
|
categoryLog.push(format.formatted);
|
||||||
// Push Sub Messages
|
// Push Sub Messages (No need for Async, Author Info Not Calculated in Sub Messages)
|
||||||
if (changelogMessage.subChangelogMessages) {
|
if (format.message.subChangelogMessages) {
|
||||||
for (const subMessage of changelogMessage.subChangelogMessages)
|
for (const subMessage of format.message.subChangelogMessages)
|
||||||
categoryLog.push(await formatChangelogMessage(subMessage, true));
|
categoryLog.push(await formatChangelogMessage(subMessage, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,58 +244,110 @@ async function formatChangelogMessage(
|
|||||||
changelogMessage.specialFormatting.storage,
|
changelogMessage.specialFormatting.storage,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (changelogMessage.commitObject && !subMessage) {
|
if (!changelogMessage.commitObject || subMessage) {
|
||||||
if (data.combineList.has(changelogMessage.commitObject.hash)) {
|
return formatMessage(message, indentation, undefined, subMessage);
|
||||||
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}))`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* 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", {
|
const date = new Date(commit.date).toLocaleDateString("en-us", {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "short",
|
month: "short",
|
||||||
day: "numeric",
|
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);
|
const shortSHA = commit.hash.substring(0, 7);
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ export interface SpecialChangelogFormatting<T> {
|
|||||||
subMessage: boolean,
|
subMessage: boolean,
|
||||||
indentation: string,
|
indentation: string,
|
||||||
storage?: T,
|
storage?: T,
|
||||||
) => string;
|
) => Promise<string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage
|
* Storage
|
||||||
|
@ -593,9 +593,15 @@ export async function getVersionManifest(
|
|||||||
*/
|
*/
|
||||||
export function cleanupVersion(version?: string): string {
|
export function cleanupVersion(version?: string): string {
|
||||||
if (!version) return "";
|
if (!version) return "";
|
||||||
|
|
||||||
|
if (!version.replace(/[\d+.?]+/g, "")) return version;
|
||||||
|
|
||||||
version = version.replace(/1\.12\.2|1\.12|\.jar/g, "");
|
version = version.replace(/1\.12\.2|1\.12|\.jar/g, "");
|
||||||
const list = version.match(/[\d+.?]+/g);
|
const list = version.match(/[\d+.?]+/g);
|
||||||
if (!list) return version;
|
if (!list) return version;
|
||||||
|
|
||||||
|
if (list[list.length - 1] == "0") return version;
|
||||||
|
|
||||||
return list[list.length - 1];
|
return list[list.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,20 +656,99 @@ export async function getIssueURL(
|
|||||||
logError(
|
logError(
|
||||||
`Failed to get the Issue/PR Info for Issue/PR #${issueNumber}. Returned Status Code ${issueInfo.status}, expected Status 200.`,
|
`Failed to get the Issue/PR Info for Issue/PR #${issueNumber}. Returned Status Code ${issueInfo.status}, expected Status 200.`,
|
||||||
);
|
);
|
||||||
|
issueURLCache.set(issueNumber, "");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
logInfo(
|
logInfo(
|
||||||
`No Issue URL Cache for Issue Number ${issueNumber}. Retrieved Specifically.`,
|
`No Issue URL Cache for Issue Number ${issueNumber}. Retrieved Specifically.`,
|
||||||
);
|
);
|
||||||
|
issueURLCache.set(issueNumber, issueInfo.data.html_url);
|
||||||
return issueInfo.data.html_url;
|
return issueInfo.data.html_url;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(
|
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.`,
|
`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 "";
|
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_VERSION_REG = /forge-(.+)/;
|
||||||
export const FORGE_MAVEN = "https://files.minecraftforge.net/maven/";
|
export const FORGE_MAVEN = "https://files.minecraftforge.net/maven/";
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user