diff --git a/src/core/github.js b/src/core/github.js index e6d14bcda1..c838bafd76 100644 --- a/src/core/github.js +++ b/src/core/github.js @@ -93,10 +93,64 @@ export async function run(conf) { } const branch = conf.github.branch || "gh-pages"; const issueBase = new URL("./issues/", ghURL).href; - const commitHistoryURL = new URL( - `./commits/${conf.github.branch ?? ""}`, - ghURL.href - ); + + // Allow custom pullsURL and commitHistoryURL for monorepo scenarios + let pullsURL; + if (typeof conf.github === "object" && !conf.github.hasOwnProperty("pullsURL")) { + pullsURL = new URL("./pulls/", ghURL).href; + } else { + pullsURL = conf.github.pullsURL; + } + + // Validate pullsURL if it's provided + if (pullsURL) { + try { + const pullsURLObj = new URL(pullsURL); + if (pullsURLObj.origin !== "https://github.com") { + const msg = docLink`${"[github.pullsURL]"} must be HTTPS and pointing to GitHub. (${pullsURL}).`; + rejectGithubPromise(msg); + return; + } + if (!pullsURLObj.pathname.includes("/pulls")) { + const msg = docLink`${"[github.pullsURL]"} must point to pull requests. (${pullsURL}).`; + rejectGithubPromise(msg); + return; + } + } catch { + const msg = docLink`${"[github.pullsURL]"} is not a valid URL. (${pullsURL}).`; + rejectGithubPromise(msg); + return; + } + } + + let commitHistoryURL; + if (typeof conf.github === "object" && !conf.github.hasOwnProperty("commitHistoryURL")) { + commitHistoryURL = new URL(`./commits/${branch}`, ghURL.href).href; + } else { + commitHistoryURL = conf.github.commitHistoryURL; + } + + // Validate commitHistoryURL if it's provided + if (commitHistoryURL) { + try { + const commitURLObj = new URL(commitHistoryURL); + if (commitURLObj.origin !== "https://github.com") { + const msg = docLink`${"[github.commitHistoryURL]"} must be HTTPS and pointing to GitHub. (${commitHistoryURL}).`; + rejectGithubPromise(msg); + return; + } + if (!commitURLObj.pathname.includes("/commit")) { + const msg = docLink`${"[github.commitHistoryURL]"} must point to commits. (${commitHistoryURL}).`; + rejectGithubPromise(msg); + return; + } + } catch { + const msg = docLink`${"[github.commitHistoryURL]"} is not a valid URL. (${commitHistoryURL}).`; + rejectGithubPromise(msg); + return; + } + } + const newProps = { edDraftURI: `https://${org.toLowerCase()}.github.io/${repo}/`, githubToken: undefined, @@ -104,7 +158,7 @@ export async function run(conf) { issueBase, atRiskBase: issueBase, otherLinks: [], - pullBase: new URL("./pulls/", ghURL).href, + pullBase: pullsURL, shortName: repo, }; // Assign new properties, but retain existing ones @@ -133,11 +187,11 @@ export async function run(conf) { }, { value: l10n.commit_history, - href: commitHistoryURL.href, + href: commitHistoryURL, }, { value: "Pull requests", - href: newProps.pullBase, + href: pullsURL, }, ], }; @@ -152,9 +206,9 @@ export async function run(conf) { apiBase: githubAPI, fullName: `${org}/${repo}`, issuesURL: issueBase, - pullsURL: newProps.pullBase, + pullsURL: pullsURL, newIssuesURL: new URL("./new/choose", issueBase).href, - commitHistoryURL: commitHistoryURL.href, + commitHistoryURL: commitHistoryURL, }; resolveGithubPromise(normalizedGHObj); diff --git a/tests/spec/core/github-spec.js b/tests/spec/core/github-spec.js index 6581b9f36b..b3e58a6b51 100644 --- a/tests/spec/core/github-spec.js +++ b/tests/spec/core/github-spec.js @@ -81,6 +81,29 @@ describe("Core - Github", () => { const doc = await makeRSDoc(opts); doesntOverrideTest(doc); }); + it("normalizes github object with custom pullsURL and commitsURL", async () => { + const opts = { + config: Object.assign(makeBasicConfig(), { + github: { + repoURL: "https://github.com/w3c/core-aam/", + pullsURL: "https://github.com/w3c/aria/pulls/", + commitsURL: "https://github.com/w3c/aria/commits/", + }, + }), + body: makeDefaultBody(), + }; + delete opts.config.edDraftURI; + delete opts.config.shortName; + + const doc = await makeRSDoc(opts); + const { respecConfig: conf } = doc.defaultView; + + // Check that the github object is normalized correctly + expect(conf.github.pullsURL).toBe("https://github.com/w3c/aria/pulls/"); + expect(conf.github.commitHistoryURL).toBe("https://github.com/w3c/aria/commits/"); + expect(conf.github.issuesURL).toBe("https://github.com/w3c/core-aam/issues/"); + expect(conf.github.repoURL).toBe("https://github.com/w3c/core-aam/"); + }); }); describe("the definition list items (localized)", () => { const l10n = { @@ -135,5 +158,52 @@ describe("Core - Github", () => { "https://github.com/speced/respec/commits/develop" ); }); + it("supports custom pullsURL and commitsURL for monorepo scenarios", async () => { + const customOpt = { + config: Object.assign(makeBasicConfig(), { + github: { + repoURL: "https://github.com/w3c/core-aam/", + pullsURL: "https://github.com/w3c/aria/pulls/", + commitsURL: "https://github.com/w3c/aria/commits/", + }, + excludeGithubLinks: false, + }), + body: makeDefaultBody(), + htmlAttrs: { + lang: "nl", + }, + }; + delete customOpt.config.edDraftURI; + delete customOpt.config.shortName; + + const doc = await makeRSDoc(customOpt); + + // Check that the custom pull request URL is used + const pullRequests = Array.from(doc.querySelectorAll("dd")).find( + elem => elem.textContent.trim() === "Pull requests" + ); + expect(pullRequests).toBeTruthy(); + expect(pullRequests.querySelector("a").href).toBe( + "https://github.com/w3c/aria/pulls/" + ); + + // Check that the custom commit history URL is used + const commitHistory = Array.from(doc.querySelectorAll("dd")).find( + elem => elem.textContent.trim() === "Revisiehistorie" + ); + expect(commitHistory).toBeTruthy(); + expect(commitHistory.querySelector("a").href).toBe( + "https://github.com/w3c/aria/commits/" + ); + + // Issue base should still use repoURL + const fileABug = Array.from(doc.querySelectorAll("dd")).find( + elem => elem.textContent.trim() === "Dien een melding in" + ); + expect(fileABug).toBeTruthy(); + expect(fileABug.querySelector("a").href).toBe( + "https://github.com/w3c/core-aam/issues/" + ); + }); }); });