Allow for Transforming Issue Tags into Links (#511)
[COMBINE] commits = ["ce5272fcf17b617495f05de102cf39dc23aece95"] [COMBINE]
This commit is contained in:
parent
123adfeccb
commit
2d6e7a647e
@ -16,3 +16,9 @@ export const configFolder = upath.join(overridesFolder, "config");
|
||||
export const configOverridesFolder = upath.join(overridesFolder, "config-overrides");
|
||||
export const rootDirectory = "..";
|
||||
export const templatesFolder = "templates";
|
||||
|
||||
// The Repository Owner (For Issues & PR Tags Transforms in Changelog)
|
||||
export const repoOwner = "Nomi-CEu";
|
||||
|
||||
// The Repository Name (For Issues & PR Tags Transforms in Changelog)
|
||||
export const repoName = "Nomi-CEu";
|
||||
|
@ -10,6 +10,8 @@ import parse from "./parser";
|
||||
import { specialParserSetup } from "./specialParser";
|
||||
import generateModChanges from "./generateModChanges";
|
||||
import pushAll, { pushChangelog, pushSeperator, pushTitle } from "./pusher";
|
||||
import log from "fancy-log";
|
||||
import * as util from "util";
|
||||
|
||||
/**
|
||||
* Generates a changelog based on environmental variables, and saves it a changelog data class.
|
||||
@ -24,6 +26,8 @@ async function createChangelog(): Promise<ChangelogData> {
|
||||
const tags = data.getIterations();
|
||||
pushTitle(data);
|
||||
for (const tag of tags) {
|
||||
const iteration = tags.indexOf(tag);
|
||||
log(`Iteration ${iteration + 1} of Changelog.`);
|
||||
data.setupIteration(tag);
|
||||
categoriesSetup();
|
||||
specialParserSetup(data);
|
||||
@ -34,8 +38,8 @@ async function createChangelog(): Promise<ChangelogData> {
|
||||
|
||||
await generateModChanges(data);
|
||||
|
||||
pushChangelog(data);
|
||||
if (tags.indexOf(tag) < tags.length - 1) {
|
||||
await pushChangelog(data);
|
||||
if (iteration < tags.length - 1) {
|
||||
// More to go
|
||||
pushSeperator(data);
|
||||
data.resetForIteration();
|
||||
@ -43,6 +47,7 @@ async function createChangelog(): Promise<ChangelogData> {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
log("No Iterations Detected.");
|
||||
|
||||
categoriesSetup();
|
||||
specialParserSetup(data);
|
||||
@ -53,7 +58,7 @@ async function createChangelog(): Promise<ChangelogData> {
|
||||
|
||||
await generateModChanges(data);
|
||||
|
||||
pushAll(data);
|
||||
await pushAll(data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { defaultIndentation, modChangesAllocations, repoLink } from "./definitio
|
||||
import ChangelogData from "./changelogData";
|
||||
import { SpecialChangelogFormatting } from "../../types/changelogTypes";
|
||||
import { sortCommitListReverse } from "./pusher";
|
||||
import { error } from "fancy-log";
|
||||
|
||||
/**
|
||||
* Mod Changes special formatting
|
||||
@ -161,7 +162,7 @@ function getCommitChange(SHA: string): CommitChange {
|
||||
oldManifest = JSON.parse(getFileAtRevision("manifest.json", `${SHA}^`)) as ModpackManifest;
|
||||
newManifest = JSON.parse(getFileAtRevision("manifest.json", SHA)) as ModpackManifest;
|
||||
} catch (e) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Failed to parse the manifest.json file at commit ${SHA} or the commit before!
|
||||
Skipping...`);
|
||||
return;
|
||||
|
@ -2,12 +2,15 @@ import ChangelogData from "./changelogData";
|
||||
import { categories, defaultIndentation } from "./definitions";
|
||||
import { Category, ChangelogMessage, Commit } from "../../types/changelogTypes";
|
||||
import { repoLink } from "./definitions";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { getIssueURL, getNewestIssueURLs } from "../../util/util";
|
||||
|
||||
let data: ChangelogData;
|
||||
let octokit: Octokit;
|
||||
|
||||
export default function pushAll(inputData: ChangelogData): void {
|
||||
export default async function pushAll(inputData: ChangelogData): Promise<void> {
|
||||
pushTitle(inputData);
|
||||
pushChangelog(inputData);
|
||||
await pushChangelog(inputData);
|
||||
}
|
||||
|
||||
export function pushTitle(inputData: ChangelogData): void {
|
||||
@ -35,15 +38,22 @@ export function pushTitle(inputData: ChangelogData): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function pushChangelog(inputData: ChangelogData): void {
|
||||
export async function pushChangelog(inputData: ChangelogData): Promise<void> {
|
||||
data = inputData;
|
||||
|
||||
octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN,
|
||||
});
|
||||
|
||||
// Save Issue/PR Info to Cache
|
||||
await getNewestIssueURLs(octokit);
|
||||
|
||||
data.builder.push(`# Changes Since ${data.since}`, "");
|
||||
|
||||
// Push Sections of Changelog
|
||||
categories.forEach((category) => {
|
||||
pushCategory(category);
|
||||
});
|
||||
for (const category of categories) {
|
||||
await pushCategory(category);
|
||||
}
|
||||
|
||||
// Push the commit log
|
||||
if (data.commitList.length > 0) {
|
||||
@ -75,12 +85,12 @@ export function pushSeperator(inputData: ChangelogData): void {
|
||||
/**
|
||||
* Pushes a given category to the builders.
|
||||
*/
|
||||
function pushCategory(category: Category) {
|
||||
async function pushCategory(category: Category) {
|
||||
const categoryLog: string[] = [];
|
||||
let hasValues = false;
|
||||
|
||||
// Push All Sub Categories
|
||||
category.subCategories.forEach((subCategory) => {
|
||||
for (const subCategory of category.subCategories) {
|
||||
// Loop through key list instead of map to produce correct order
|
||||
const list = category.changelogSection.get(subCategory);
|
||||
if (list && list.length != 0) {
|
||||
@ -97,19 +107,18 @@ function pushCategory(category: Category) {
|
||||
);
|
||||
|
||||
// Push Log
|
||||
list.forEach((changelogMessage) => {
|
||||
categoryLog.push(formatChangelogMessage(changelogMessage));
|
||||
for (const changelogMessage of list) {
|
||||
categoryLog.push(await formatChangelogMessage(changelogMessage));
|
||||
// Push Sub Messages
|
||||
if (changelogMessage.subChangelogMessages) {
|
||||
changelogMessage.subChangelogMessages.forEach((subMessage) => {
|
||||
categoryLog.push(formatChangelogMessage(subMessage, true));
|
||||
});
|
||||
for (const subMessage of changelogMessage.subChangelogMessages)
|
||||
categoryLog.push(await formatChangelogMessage(subMessage, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
categoryLog.push("");
|
||||
hasValues = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (hasValues) {
|
||||
// Push Title
|
||||
data.builder.push(`## ${category.categoryName}:`);
|
||||
@ -164,22 +173,15 @@ export function sortCommitListReverse(list: Commit[]): void {
|
||||
* @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
|
||||
*/
|
||||
function formatChangelogMessage(changelogMessage: ChangelogMessage, subMessage = false): string {
|
||||
async function formatChangelogMessage(changelogMessage: ChangelogMessage, subMessage = false): Promise<string> {
|
||||
if (changelogMessage.specialFormatting)
|
||||
return changelogMessage.specialFormatting.formatting(changelogMessage, changelogMessage.specialFormatting.storage);
|
||||
|
||||
const indentation = changelogMessage.indentation == undefined ? defaultIndentation : changelogMessage.indentation;
|
||||
let message = changelogMessage.commitMessage.trim();
|
||||
|
||||
// Transform PR tags into a link.
|
||||
if (message.match(/\(#\d+\)/g)) {
|
||||
const matched = message.match(/\(#\d+\)/g);
|
||||
matched.forEach((match) => {
|
||||
// Extract digits
|
||||
const digits = match.match(/\d+/g);
|
||||
message = message.replace(match, `([#${digits}](${repoLink}pull/${digits}))`);
|
||||
});
|
||||
}
|
||||
// Transform PR and/or Issue tags into a link.
|
||||
message = await transformTags(message);
|
||||
|
||||
if (changelogMessage.commitObject && !subMessage) {
|
||||
if (data.combineList.has(changelogMessage.commitObject.hash)) {
|
||||
@ -228,3 +230,23 @@ function formatCommit(commit: Commit): string {
|
||||
|
||||
return `* [\`${shortSHA}\`](${repoLink}commit/${commit.hash}): ${formattedCommit}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms PR/Issue Tags into Links.
|
||||
*/
|
||||
async function transformTags(message: string): Promise<string> {
|
||||
if (message.search(/#\d+/) !== -1) {
|
||||
const matched = message.match(/#\d+/g);
|
||||
for (const match of matched) {
|
||||
// Extract digits
|
||||
const digits = Number.parseInt(match.match(/\d+/)[0]);
|
||||
|
||||
// Get PR/Issue Info (PRs are listed in the Issue API Endpoint)
|
||||
const url = await getIssueURL(digits, octokit);
|
||||
if (url) {
|
||||
message = message.replace(match, `[#${digits}](${url})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
} from "./definitions";
|
||||
import { findCategories, findSubCategory } from "./parser";
|
||||
import ChangelogData from "./changelogData";
|
||||
import { error } from "fancy-log";
|
||||
|
||||
let data: ChangelogData;
|
||||
|
||||
@ -46,7 +47,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
|
||||
if (!info) return undefined;
|
||||
|
||||
if (!info.checks) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Ignore Info in body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
@ -58,7 +59,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
|
||||
try {
|
||||
infoKeys = Object.keys(info.checks);
|
||||
} catch (err) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Could not get the keys in Ignore Info of body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
@ -73,7 +74,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
|
||||
infoKeys.forEach((key) => {
|
||||
if (ignoreKeys.has(key)) checkResults.push(ignoreChecks[key].call(this, info.checks[key], data));
|
||||
else {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Ignore Check with key '${key}' in body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
@ -85,7 +86,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
|
||||
}
|
||||
});
|
||||
if (checkResults.length === 0) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
No Ignore Checks found in body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
@ -102,7 +103,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
|
||||
if (info.logic === undefined) logic = defaultIgnoreLogic;
|
||||
else if (Object.keys(ignoreLogics).includes(info.logic)) logic = ignoreLogics[info.logic];
|
||||
else {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Ignore Logic '${info.logic}' in body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
@ -290,7 +291,7 @@ async function parseTOML<T>(
|
||||
if (!itemKey) item = parseResult.data as T;
|
||||
else item = parseResult.data[itemKey];
|
||||
} catch (e) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Failed parsing TOML in body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
@ -298,13 +299,13 @@ async function parseTOML<T>(
|
||||
This could be because of invalid syntax.`);
|
||||
|
||||
if (commitObject.body && commitBody !== commitObject.body) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Original Body:
|
||||
\`\`\`
|
||||
${commitObject.body}\`\`\``);
|
||||
}
|
||||
|
||||
console.error(`\n${endMessage}\n`);
|
||||
error(`\n${endMessage}\n`);
|
||||
if (data.isTest) throw e;
|
||||
return undefined;
|
||||
}
|
||||
@ -335,19 +336,19 @@ async function parseTOMLToList<T>(
|
||||
const endMessage = getEndMessage(delimiter);
|
||||
|
||||
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
List (key: '${listKey}') in body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
of commit object ${commitObject.hash} (${commitObject.message}) is empty, not a list, or does not exist.`);
|
||||
|
||||
if (commitObject.body && commitBody !== commitObject.body) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Original Body:
|
||||
\`\`\`
|
||||
${commitObject.body}\`\`\``);
|
||||
}
|
||||
console.error(`${endMessage}\n`);
|
||||
error(`${endMessage}\n`);
|
||||
|
||||
if (data.isTest) throw new Error("Failed Parsing Message List. See Above.");
|
||||
return;
|
||||
@ -355,19 +356,19 @@ async function parseTOMLToList<T>(
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const item = messages[i];
|
||||
if (!emptyCheck(item)) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Missing Requirements for entry ${i + 1} in body:
|
||||
\`\`\`
|
||||
${commitBody}\`\`\`
|
||||
of commit object ${commitObject.hash} (${commitObject.message}).`);
|
||||
|
||||
if (commitObject.body && commitBody !== commitObject.body) {
|
||||
console.error(dedent`
|
||||
error(dedent`
|
||||
Original Body:
|
||||
\`\`\`
|
||||
${commitObject.body}\`\`\``);
|
||||
}
|
||||
console.error(`${endMessage}\n`);
|
||||
error(`${endMessage}\n`);
|
||||
|
||||
if (data.isTest) throw new Error("Bad Entry. See Above.");
|
||||
continue;
|
||||
|
@ -6,6 +6,7 @@ import gulp from "gulp";
|
||||
import dedent from "dedent-js";
|
||||
import { checkEnvironmentalVariables } from "../../util/util";
|
||||
import sortedStringify from "json-stable-stringify-without-jsonify";
|
||||
import log, { error } from "fancy-log";
|
||||
|
||||
// This updates all the files, for a release.
|
||||
|
||||
@ -30,11 +31,11 @@ export async function check(): Promise<void> {
|
||||
const versionsFilePath: string = upath.join(templatesFolder, "versions.txt");
|
||||
|
||||
if (notRelease) {
|
||||
console.log("Detected that this is not a release commit.");
|
||||
console.log("Version info will not change, but the files will be updated from the template.");
|
||||
log("Detected that this is not a release commit.");
|
||||
log("Version info will not change, but the files will be updated from the template.");
|
||||
await checkNotRelease(versionsFilePath);
|
||||
} else {
|
||||
console.log("Detected that this is a release commit.");
|
||||
log("Detected that this is a release commit.");
|
||||
await checkRelease(versionsFilePath);
|
||||
}
|
||||
}
|
||||
@ -50,9 +51,7 @@ export async function setNotRelease(): Promise<void> {
|
||||
async function checkNotRelease(versionsFilePath: string) {
|
||||
// Check if versions.txt exists
|
||||
if (!fs.existsSync(versionsFilePath)) {
|
||||
console.error(
|
||||
`Version.txt does not exist. Creating empty file, and adding ${version} to it. This may be an error.`,
|
||||
);
|
||||
error(`Version.txt does not exist. Creating empty file, and adding ${version} to it. This may be an error.`);
|
||||
|
||||
// Create Versions.txt, with version
|
||||
await fs.promises.writeFile(versionsFilePath, ` - ${version}`);
|
||||
@ -62,7 +61,7 @@ async function checkNotRelease(versionsFilePath: string) {
|
||||
|
||||
// No Duplicate Key
|
||||
if (!versionList.includes(version)) {
|
||||
console.error(`Version is not in version.txt. Adding ${version} to version.txt. This may be an error.`);
|
||||
error(`Version is not in version.txt. Adding ${version} to version.txt. This may be an error.`);
|
||||
|
||||
versionList = ` - ${version}\n${versionList}`;
|
||||
await fs.promises.writeFile(versionsFilePath, versionList);
|
||||
@ -74,7 +73,7 @@ async function checkNotRelease(versionsFilePath: string) {
|
||||
async function checkRelease(versionsFilePath: string) {
|
||||
// Check if versions.txt exists
|
||||
if (!fs.existsSync(versionsFilePath)) {
|
||||
console.error("Version.txt does not exist. Creating empty file. This may be an error.");
|
||||
error("Version.txt does not exist. Creating empty file. This may be an error.");
|
||||
|
||||
// Create Versions.txt
|
||||
fs.closeSync(fs.openSync(versionsFilePath, "w"));
|
||||
|
@ -85,12 +85,12 @@ async function fetchExternalDependencies() {
|
||||
*/
|
||||
async function fetchOrMakeChangelog() {
|
||||
if (isEnvVariableSet("CHANGELOG_URL") && isEnvVariableSet("CHANGELOG_CF_URL")) {
|
||||
console.log("Using Changelog Files from URL.");
|
||||
log("Using Changelog Files from URL.");
|
||||
await downloadChangelogs(process.env.CHANGELOG_URL, process.env.CHANGELOG_CF_URL);
|
||||
return;
|
||||
}
|
||||
if (isEnvVariableSet("CHANGELOG_BRANCH")) {
|
||||
console.log("Using Changelog Files from Branch.");
|
||||
log("Using Changelog Files from Branch.");
|
||||
const url = "https://raw.githubusercontent.com/Nomi-CEu/Nomi-CEu/{{ branch }}/{{ filename }}";
|
||||
await downloadChangelogs(
|
||||
mustache.render(url, { branch: process.env.CHANGELOG_BRANCH, filename: "CHANGELOG.md" }),
|
||||
@ -98,7 +98,7 @@ async function fetchOrMakeChangelog() {
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.log("Creating Changelog Files.");
|
||||
log("Creating Changelog Files.");
|
||||
await createBuildChangelog();
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,11 @@ import { fetchFileInfo, fetchProject, fetchProjectsBulk } from "./curseForgeAPI"
|
||||
import Bluebird from "bluebird";
|
||||
import { VersionManifest } from "../types/versionManifest";
|
||||
import { VersionsManifest } from "../types/versionsManifest";
|
||||
import log from "fancy-log";
|
||||
import log, { error } from "fancy-log";
|
||||
import { pathspec, SimpleGit, simpleGit } from "simple-git";
|
||||
import { Commit, ModChangeInfo } from "../types/changelogTypes";
|
||||
import { rootDirectory } from "../globals";
|
||||
import { repoName, repoOwner, rootDirectory } from "../globals";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
|
||||
const LIBRARY_REG = /^(.+?):(.+?):(.+?)$/;
|
||||
|
||||
@ -238,7 +239,7 @@ export async function getChangelog(since = "HEAD", to = "HEAD", dirs: string[] =
|
||||
const commitList: Commit[] = [];
|
||||
await git.log(options, (err, output) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
error(err);
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
@ -443,3 +444,57 @@ export function cleanupVersion(version: string): string {
|
||||
const list = version.match(/[\d+.?]+/g);
|
||||
return list[list.length - 1];
|
||||
}
|
||||
|
||||
const issueURLCache: Map<number, string> = new Map<number, string>();
|
||||
|
||||
/**
|
||||
* Gets newest updated 100 closed issue/PR URLs of the repo and saves it to the cache.
|
||||
*/
|
||||
export async function getNewestIssueURLs(octokit: Octokit): Promise<void> {
|
||||
if (issueURLCache.size > 0) return;
|
||||
try {
|
||||
const issues = await octokit.issues.listForRepo({
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
per_page: 100,
|
||||
state: "closed",
|
||||
sort: "updated",
|
||||
});
|
||||
if (issues.status !== 200) {
|
||||
error(`Failed to get all Issue URLs of Repo. Returned Status Code ${issues.status}, expected Status 200.`);
|
||||
return;
|
||||
}
|
||||
issues.data.forEach((issue) => {
|
||||
if (!issueURLCache.has(issue.number)) issueURLCache.set(issue.number, issue.html_url);
|
||||
});
|
||||
} catch (e) {
|
||||
error("Failed to get all Issue URLs of Repo. This may be because there are no issues, or because of rate limits.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the specified Issue URL from the cache, or retrieves it.
|
||||
*/
|
||||
export async function getIssueURL(issueNumber: number, octokit: Octokit): Promise<string> {
|
||||
if (issueURLCache.has(issueNumber)) return issueURLCache.get(issueNumber);
|
||||
try {
|
||||
const issueInfo = await octokit.issues.get({
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
if (issueInfo.status !== 200) {
|
||||
error(
|
||||
`Failed to get the Issue/PR Info for Issue/PR #${issueNumber}. Returned Status Code ${issueInfo.status}, expected Status 200.`,
|
||||
);
|
||||
return "";
|
||||
}
|
||||
log(`No Issue URL Cache for Issue Number ${issueNumber}. Retrieved Specifically.`);
|
||||
return issueInfo.data.html_url;
|
||||
} catch (e) {
|
||||
error(
|
||||
`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.`,
|
||||
);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user