Allow for Transforming Issue Tags into Links (#511)

[COMBINE]
commits = ["ce5272fcf17b617495f05de102cf39dc23aece95"]
[COMBINE]
This commit is contained in:
Integer Limit 2023-11-01 08:26:24 +11:00 committed by GitHub
parent 123adfeccb
commit 2d6e7a647e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 146 additions and 57 deletions

View File

@ -16,3 +16,9 @@ export const configFolder = upath.join(overridesFolder, "config");
export const configOverridesFolder = upath.join(overridesFolder, "config-overrides"); export const configOverridesFolder = upath.join(overridesFolder, "config-overrides");
export const rootDirectory = ".."; export const rootDirectory = "..";
export const templatesFolder = "templates"; 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";

View File

@ -10,6 +10,8 @@ import parse from "./parser";
import { specialParserSetup } from "./specialParser"; import { specialParserSetup } from "./specialParser";
import generateModChanges from "./generateModChanges"; import generateModChanges from "./generateModChanges";
import pushAll, { pushChangelog, pushSeperator, pushTitle } from "./pusher"; 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. * 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(); const tags = data.getIterations();
pushTitle(data); pushTitle(data);
for (const tag of tags) { for (const tag of tags) {
const iteration = tags.indexOf(tag);
log(`Iteration ${iteration + 1} of Changelog.`);
data.setupIteration(tag); data.setupIteration(tag);
categoriesSetup(); categoriesSetup();
specialParserSetup(data); specialParserSetup(data);
@ -34,8 +38,8 @@ async function createChangelog(): Promise<ChangelogData> {
await generateModChanges(data); await generateModChanges(data);
pushChangelog(data); await pushChangelog(data);
if (tags.indexOf(tag) < tags.length - 1) { if (iteration < tags.length - 1) {
// More to go // More to go
pushSeperator(data); pushSeperator(data);
data.resetForIteration(); data.resetForIteration();
@ -43,6 +47,7 @@ async function createChangelog(): Promise<ChangelogData> {
} }
return data; return data;
} }
log("No Iterations Detected.");
categoriesSetup(); categoriesSetup();
specialParserSetup(data); specialParserSetup(data);
@ -53,7 +58,7 @@ async function createChangelog(): Promise<ChangelogData> {
await generateModChanges(data); await generateModChanges(data);
pushAll(data); await pushAll(data);
return data; return data;
} }

View File

@ -8,6 +8,7 @@ import { defaultIndentation, modChangesAllocations, repoLink } from "./definitio
import ChangelogData from "./changelogData"; import ChangelogData from "./changelogData";
import { SpecialChangelogFormatting } from "../../types/changelogTypes"; import { SpecialChangelogFormatting } from "../../types/changelogTypes";
import { sortCommitListReverse } from "./pusher"; import { sortCommitListReverse } from "./pusher";
import { error } from "fancy-log";
/** /**
* Mod Changes special formatting * Mod Changes special formatting
@ -161,7 +162,7 @@ function getCommitChange(SHA: string): CommitChange {
oldManifest = JSON.parse(getFileAtRevision("manifest.json", `${SHA}^`)) as ModpackManifest; oldManifest = JSON.parse(getFileAtRevision("manifest.json", `${SHA}^`)) as ModpackManifest;
newManifest = JSON.parse(getFileAtRevision("manifest.json", SHA)) as ModpackManifest; newManifest = JSON.parse(getFileAtRevision("manifest.json", SHA)) as ModpackManifest;
} catch (e) { } catch (e) {
console.error(dedent` error(dedent`
Failed to parse the manifest.json file at commit ${SHA} or the commit before! Failed to parse the manifest.json file at commit ${SHA} or the commit before!
Skipping...`); Skipping...`);
return; return;

View File

@ -2,12 +2,15 @@ import ChangelogData from "./changelogData";
import { categories, defaultIndentation } from "./definitions"; import { categories, defaultIndentation } from "./definitions";
import { Category, ChangelogMessage, Commit } from "../../types/changelogTypes"; import { Category, ChangelogMessage, Commit } from "../../types/changelogTypes";
import { repoLink } from "./definitions"; import { repoLink } from "./definitions";
import { Octokit } from "@octokit/rest";
import { getIssueURL, getNewestIssueURLs } from "../../util/util";
let data: ChangelogData; let data: ChangelogData;
let octokit: Octokit;
export default function pushAll(inputData: ChangelogData): void { export default async function pushAll(inputData: ChangelogData): Promise<void> {
pushTitle(inputData); pushTitle(inputData);
pushChangelog(inputData); await pushChangelog(inputData);
} }
export function pushTitle(inputData: ChangelogData): void { 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; 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}`, ""); data.builder.push(`# Changes Since ${data.since}`, "");
// Push Sections of Changelog // Push Sections of Changelog
categories.forEach((category) => { for (const category of categories) {
pushCategory(category); await pushCategory(category);
}); }
// Push the commit log // Push the commit log
if (data.commitList.length > 0) { if (data.commitList.length > 0) {
@ -75,12 +85,12 @@ export function pushSeperator(inputData: ChangelogData): void {
/** /**
* Pushes a given category to the builders. * Pushes a given category to the builders.
*/ */
function pushCategory(category: Category) { async function pushCategory(category: Category) {
const categoryLog: string[] = []; const categoryLog: string[] = [];
let hasValues = false; let hasValues = false;
// Push All Sub Categories // 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 // Loop through key list instead of map to produce correct order
const list = category.changelogSection.get(subCategory); const list = category.changelogSection.get(subCategory);
if (list && list.length != 0) { if (list && list.length != 0) {
@ -97,19 +107,18 @@ function pushCategory(category: Category) {
); );
// Push Log // Push Log
list.forEach((changelogMessage) => { for (const changelogMessage of list) {
categoryLog.push(formatChangelogMessage(changelogMessage)); categoryLog.push(await formatChangelogMessage(changelogMessage));
// Push Sub Messages // Push Sub Messages
if (changelogMessage.subChangelogMessages) { if (changelogMessage.subChangelogMessages) {
changelogMessage.subChangelogMessages.forEach((subMessage) => { for (const subMessage of changelogMessage.subChangelogMessages)
categoryLog.push(formatChangelogMessage(subMessage, true)); categoryLog.push(await formatChangelogMessage(subMessage, true));
});
} }
}); }
categoryLog.push(""); categoryLog.push("");
hasValues = true; hasValues = true;
} }
}); }
if (hasValues) { if (hasValues) {
// Push Title // Push Title
data.builder.push(`## ${category.categoryName}:`); 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. * @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 * @return string Formatted Changelog Message
*/ */
function formatChangelogMessage(changelogMessage: ChangelogMessage, subMessage = false): string { async function formatChangelogMessage(changelogMessage: ChangelogMessage, subMessage = false): Promise<string> {
if (changelogMessage.specialFormatting) if (changelogMessage.specialFormatting)
return changelogMessage.specialFormatting.formatting(changelogMessage, changelogMessage.specialFormatting.storage); return changelogMessage.specialFormatting.formatting(changelogMessage, changelogMessage.specialFormatting.storage);
const indentation = changelogMessage.indentation == undefined ? defaultIndentation : changelogMessage.indentation; const indentation = changelogMessage.indentation == undefined ? defaultIndentation : changelogMessage.indentation;
let message = changelogMessage.commitMessage.trim(); let message = changelogMessage.commitMessage.trim();
// Transform PR tags into a link. // Transform PR and/or Issue tags into a link.
if (message.match(/\(#\d+\)/g)) { message = await transformTags(message);
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}))`);
});
}
if (changelogMessage.commitObject && !subMessage) { if (changelogMessage.commitObject && !subMessage) {
if (data.combineList.has(changelogMessage.commitObject.hash)) { if (data.combineList.has(changelogMessage.commitObject.hash)) {
@ -228,3 +230,23 @@ function formatCommit(commit: Commit): string {
return `* [\`${shortSHA}\`](${repoLink}commit/${commit.hash}): ${formattedCommit}`; 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;
}

View File

@ -28,6 +28,7 @@ import {
} from "./definitions"; } from "./definitions";
import { findCategories, findSubCategory } from "./parser"; import { findCategories, findSubCategory } from "./parser";
import ChangelogData from "./changelogData"; import ChangelogData from "./changelogData";
import { error } from "fancy-log";
let data: ChangelogData; let data: ChangelogData;
@ -46,7 +47,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
if (!info) return undefined; if (!info) return undefined;
if (!info.checks) { if (!info.checks) {
console.error(dedent` error(dedent`
Ignore Info in body: Ignore Info in body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
@ -58,7 +59,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
try { try {
infoKeys = Object.keys(info.checks); infoKeys = Object.keys(info.checks);
} catch (err) { } catch (err) {
console.error(dedent` error(dedent`
Could not get the keys in Ignore Info of body: Could not get the keys in Ignore Info of body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
@ -73,7 +74,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
infoKeys.forEach((key) => { infoKeys.forEach((key) => {
if (ignoreKeys.has(key)) checkResults.push(ignoreChecks[key].call(this, info.checks[key], data)); if (ignoreKeys.has(key)) checkResults.push(ignoreChecks[key].call(this, info.checks[key], data));
else { else {
console.error(dedent` error(dedent`
Ignore Check with key '${key}' in body: Ignore Check with key '${key}' in body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
@ -85,7 +86,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
} }
}); });
if (checkResults.length === 0) { if (checkResults.length === 0) {
console.error(dedent` error(dedent`
No Ignore Checks found in body: No Ignore Checks found in body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
@ -102,7 +103,7 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro
if (info.logic === undefined) logic = defaultIgnoreLogic; if (info.logic === undefined) logic = defaultIgnoreLogic;
else if (Object.keys(ignoreLogics).includes(info.logic)) logic = ignoreLogics[info.logic]; else if (Object.keys(ignoreLogics).includes(info.logic)) logic = ignoreLogics[info.logic];
else { else {
console.error(dedent` error(dedent`
Ignore Logic '${info.logic}' in body: Ignore Logic '${info.logic}' in body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
@ -290,7 +291,7 @@ async function parseTOML<T>(
if (!itemKey) item = parseResult.data as T; if (!itemKey) item = parseResult.data as T;
else item = parseResult.data[itemKey]; else item = parseResult.data[itemKey];
} catch (e) { } catch (e) {
console.error(dedent` error(dedent`
Failed parsing TOML in body: Failed parsing TOML in body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
@ -298,13 +299,13 @@ async function parseTOML<T>(
This could be because of invalid syntax.`); This could be because of invalid syntax.`);
if (commitObject.body && commitBody !== commitObject.body) { if (commitObject.body && commitBody !== commitObject.body) {
console.error(dedent` error(dedent`
Original Body: Original Body:
\`\`\` \`\`\`
${commitObject.body}\`\`\``); ${commitObject.body}\`\`\``);
} }
console.error(`\n${endMessage}\n`); error(`\n${endMessage}\n`);
if (data.isTest) throw e; if (data.isTest) throw e;
return undefined; return undefined;
} }
@ -335,19 +336,19 @@ async function parseTOMLToList<T>(
const endMessage = getEndMessage(delimiter); const endMessage = getEndMessage(delimiter);
if (!messages || !Array.isArray(messages) || messages.length === 0) { if (!messages || !Array.isArray(messages) || messages.length === 0) {
console.error(dedent` error(dedent`
List (key: '${listKey}') in body: List (key: '${listKey}') in body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
of commit object ${commitObject.hash} (${commitObject.message}) is empty, not a list, or does not exist.`); of commit object ${commitObject.hash} (${commitObject.message}) is empty, not a list, or does not exist.`);
if (commitObject.body && commitBody !== commitObject.body) { if (commitObject.body && commitBody !== commitObject.body) {
console.error(dedent` error(dedent`
Original Body: Original Body:
\`\`\` \`\`\`
${commitObject.body}\`\`\``); ${commitObject.body}\`\`\``);
} }
console.error(`${endMessage}\n`); error(`${endMessage}\n`);
if (data.isTest) throw new Error("Failed Parsing Message List. See Above."); if (data.isTest) throw new Error("Failed Parsing Message List. See Above.");
return; return;
@ -355,19 +356,19 @@ async function parseTOMLToList<T>(
for (let i = 0; i < messages.length; i++) { for (let i = 0; i < messages.length; i++) {
const item = messages[i]; const item = messages[i];
if (!emptyCheck(item)) { if (!emptyCheck(item)) {
console.error(dedent` error(dedent`
Missing Requirements for entry ${i + 1} in body: Missing Requirements for entry ${i + 1} in body:
\`\`\` \`\`\`
${commitBody}\`\`\` ${commitBody}\`\`\`
of commit object ${commitObject.hash} (${commitObject.message}).`); of commit object ${commitObject.hash} (${commitObject.message}).`);
if (commitObject.body && commitBody !== commitObject.body) { if (commitObject.body && commitBody !== commitObject.body) {
console.error(dedent` error(dedent`
Original Body: Original Body:
\`\`\` \`\`\`
${commitObject.body}\`\`\``); ${commitObject.body}\`\`\``);
} }
console.error(`${endMessage}\n`); error(`${endMessage}\n`);
if (data.isTest) throw new Error("Bad Entry. See Above."); if (data.isTest) throw new Error("Bad Entry. See Above.");
continue; continue;

View File

@ -6,6 +6,7 @@ import gulp from "gulp";
import dedent from "dedent-js"; import dedent from "dedent-js";
import { checkEnvironmentalVariables } from "../../util/util"; import { checkEnvironmentalVariables } from "../../util/util";
import sortedStringify from "json-stable-stringify-without-jsonify"; import sortedStringify from "json-stable-stringify-without-jsonify";
import log, { error } from "fancy-log";
// This updates all the files, for a release. // 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"); const versionsFilePath: string = upath.join(templatesFolder, "versions.txt");
if (notRelease) { if (notRelease) {
console.log("Detected that this is not a release commit."); 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("Version info will not change, but the files will be updated from the template.");
await checkNotRelease(versionsFilePath); await checkNotRelease(versionsFilePath);
} else { } else {
console.log("Detected that this is a release commit."); log("Detected that this is a release commit.");
await checkRelease(versionsFilePath); await checkRelease(versionsFilePath);
} }
} }
@ -50,9 +51,7 @@ export async function setNotRelease(): Promise<void> {
async function checkNotRelease(versionsFilePath: string) { async function checkNotRelease(versionsFilePath: string) {
// Check if versions.txt exists // Check if versions.txt exists
if (!fs.existsSync(versionsFilePath)) { if (!fs.existsSync(versionsFilePath)) {
console.error( error(`Version.txt does not exist. Creating empty file, and adding ${version} to it. This may be an error.`);
`Version.txt does not exist. Creating empty file, and adding ${version} to it. This may be an error.`,
);
// Create Versions.txt, with version // Create Versions.txt, with version
await fs.promises.writeFile(versionsFilePath, ` - ${version}`); await fs.promises.writeFile(versionsFilePath, ` - ${version}`);
@ -62,7 +61,7 @@ async function checkNotRelease(versionsFilePath: string) {
// No Duplicate Key // No Duplicate Key
if (!versionList.includes(version)) { 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}`; versionList = ` - ${version}\n${versionList}`;
await fs.promises.writeFile(versionsFilePath, versionList); await fs.promises.writeFile(versionsFilePath, versionList);
@ -74,7 +73,7 @@ async function checkNotRelease(versionsFilePath: string) {
async function checkRelease(versionsFilePath: string) { async function checkRelease(versionsFilePath: string) {
// Check if versions.txt exists // Check if versions.txt exists
if (!fs.existsSync(versionsFilePath)) { 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 // Create Versions.txt
fs.closeSync(fs.openSync(versionsFilePath, "w")); fs.closeSync(fs.openSync(versionsFilePath, "w"));

View File

@ -85,12 +85,12 @@ async function fetchExternalDependencies() {
*/ */
async function fetchOrMakeChangelog() { async function fetchOrMakeChangelog() {
if (isEnvVariableSet("CHANGELOG_URL") && isEnvVariableSet("CHANGELOG_CF_URL")) { 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); await downloadChangelogs(process.env.CHANGELOG_URL, process.env.CHANGELOG_CF_URL);
return; return;
} }
if (isEnvVariableSet("CHANGELOG_BRANCH")) { 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 }}"; const url = "https://raw.githubusercontent.com/Nomi-CEu/Nomi-CEu/{{ branch }}/{{ filename }}";
await downloadChangelogs( await downloadChangelogs(
mustache.render(url, { branch: process.env.CHANGELOG_BRANCH, filename: "CHANGELOG.md" }), mustache.render(url, { branch: process.env.CHANGELOG_BRANCH, filename: "CHANGELOG.md" }),
@ -98,7 +98,7 @@ async function fetchOrMakeChangelog() {
); );
return; return;
} }
console.log("Creating Changelog Files."); log("Creating Changelog Files.");
await createBuildChangelog(); await createBuildChangelog();
} }

View File

@ -13,10 +13,11 @@ import { fetchFileInfo, fetchProject, fetchProjectsBulk } from "./curseForgeAPI"
import Bluebird from "bluebird"; import Bluebird from "bluebird";
import { VersionManifest } from "../types/versionManifest"; import { VersionManifest } from "../types/versionManifest";
import { VersionsManifest } from "../types/versionsManifest"; import { VersionsManifest } from "../types/versionsManifest";
import log from "fancy-log"; import log, { error } from "fancy-log";
import { pathspec, SimpleGit, simpleGit } from "simple-git"; import { pathspec, SimpleGit, simpleGit } from "simple-git";
import { Commit, ModChangeInfo } from "../types/changelogTypes"; import { Commit, ModChangeInfo } from "../types/changelogTypes";
import { rootDirectory } from "../globals"; import { repoName, repoOwner, rootDirectory } from "../globals";
import { Octokit } from "@octokit/rest";
const LIBRARY_REG = /^(.+?):(.+?):(.+?)$/; const LIBRARY_REG = /^(.+?):(.+?):(.+?)$/;
@ -238,7 +239,7 @@ export async function getChangelog(since = "HEAD", to = "HEAD", dirs: string[] =
const commitList: Commit[] = []; const commitList: Commit[] = [];
await git.log(options, (err, output) => { await git.log(options, (err, output) => {
if (err) { if (err) {
console.error(err); error(err);
throw new Error(); throw new Error();
} }
@ -443,3 +444,57 @@ export function cleanupVersion(version: string): string {
const list = version.match(/[\d+.?]+/g); const list = version.match(/[\d+.?]+/g);
return list[list.length - 1]; 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 "";
}
}